Add the option to hide games in the app list

Fixes #640
This commit is contained in:
Cameron Gutman 2020-08-01 18:20:39 -07:00
parent 105ad3317d
commit e8fc91191f
4 changed files with 128 additions and 18 deletions

View File

@ -2,6 +2,7 @@ package com.limelight;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.HashSet;
import java.util.List; import java.util.List;
import com.limelight.computers.ComputerManagerListener; import com.limelight.computers.ComputerManagerListener;
@ -26,6 +27,7 @@ import android.app.Service;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
@ -59,6 +61,8 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
private int lastRunningAppId; private int lastRunningAppId;
private boolean suspendGridUpdates; private boolean suspendGridUpdates;
private boolean inForeground; private boolean inForeground;
private boolean showHiddenApps;
private HashSet<Integer> hiddenAppIds = new HashSet<>();
private final static int START_OR_RESUME_ID = 1; private final static int START_OR_RESUME_ID = 1;
private final static int QUIT_ID = 2; 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 START_WITH_QUIT = 4;
private final static int VIEW_DETAILS_ID = 5; private final static int VIEW_DETAILS_ID = 5;
private final static int CREATE_SHORTCUT_ID = 6; 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 NAME_EXTRA = "Name";
public final static String UUID_EXTRA = "UUID"; public final static String UUID_EXTRA = "UUID";
public final static String NEW_PAIR_EXTRA = "NewPair"; public final static String NEW_PAIR_EXTRA = "NewPair";
public final static String SHOW_HIDDEN_APPS_EXTRA = "ShowHiddenApps";
private ComputerManagerService.ComputerManagerBinder managerBinder; private ComputerManagerService.ComputerManagerBinder managerBinder;
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
@ -98,13 +106,16 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
try { try {
appGridAdapter = new AppGridAdapter(AppView.this, appGridAdapter = new AppGridAdapter(AppView.this,
PreferenceConfiguration.readPreferences(AppView.this), PreferenceConfiguration.readPreferences(AppView.this),
computer, localBinder.getUniqueId()); computer, localBinder.getUniqueId(),
showHiddenApps);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
finish(); finish();
return; return;
} }
appGridAdapter.updateHiddenApps(hiddenAppIds);
// Now make the binder visible. We must do this after appGridAdapter // Now make the binder visible. We must do this after appGridAdapter
// is set to prevent us from reaching updateUiWithServerinfo() and // is set to prevent us from reaching updateUiWithServerinfo() and
// touching the appGridAdapter prior to initialization. // touching the appGridAdapter prior to initialization.
@ -285,8 +296,14 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
UiHelper.notifyNewRootView(this); UiHelper.notifyNewRootView(this);
showHiddenApps = getIntent().getBooleanExtra(SHOW_HIDDEN_APPS_EXTRA, false);
uuidString = getIntent().getStringExtra(UUID_EXTRA); uuidString = getIntent().getStringExtra(UUID_EXTRA);
SharedPreferences hiddenAppsPrefs = getSharedPreferences(HIDDEN_APPS_PREF_FILENAME, MODE_PRIVATE);
for (String hiddenAppIdStr : hiddenAppsPrefs.getStringSet(uuidString, new HashSet<String>())) {
hiddenAppIds.add(Integer.parseInt(hiddenAppIdStr));
}
String computerName = getIntent().getStringExtra(NAME_EXTRA); String computerName = getIntent().getStringExtra(NAME_EXTRA);
TextView label = findViewById(R.id.appListText); TextView label = findViewById(R.id.appListText);
@ -298,6 +315,21 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
Service.BIND_AUTO_CREATE); Service.BIND_AUTO_CREATE);
} }
private void updateHiddenApps() {
HashSet<String> 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() { private void populateAppGridWithCache() {
try { try {
// Try to load from cache // 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, 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Only add an option to create shortcut if box art is loaded // 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(); BitmapDrawable drawable = (BitmapDrawable)appImageView.getDrawable();
if (drawable != null && drawable.getBitmap() != null) { if (drawable != null && drawable.getBitmap() != null) {
// We have a bitmap loaded too // 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); Dialog.displayDialog(AppView.this, getResources().getString(R.string.title_details), app.app.toString(), false);
return true; 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: case CREATE_SHORTCUT_ID:
ImageView appImageView = info.targetView.findViewById(R.id.grid_image); ImageView appImageView = info.targetView.findViewById(R.id.grid_image);
Bitmap appBits = ((BitmapDrawable)appImageView.getDrawable()).getBitmap(); Bitmap appBits = ((BitmapDrawable)appImageView.getDrawable()).getBitmap();
@ -593,6 +642,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
public static class AppObject { public static class AppObject {
public final NvApp app; public final NvApp app;
public boolean isRunning; public boolean isRunning;
public boolean isHidden;
public AppObject(NvApp app) { public AppObject(NvApp app) {
if (app == null) { if (app == null) {

View File

@ -117,6 +117,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
private final static int RESUME_ID = 6; private final static int RESUME_ID = 6;
private final static int QUIT_ID = 7; private final static int QUIT_ID = 7;
private final static int VIEW_DETAILS_ID = 8; private final static int VIEW_DETAILS_ID = 8;
private final static int FULL_APP_LIST_ID = 9;
private void initializeViews() { private void initializeViews() {
setContentView(R.layout.activity_pc_view); 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, 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 // 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 // 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 @Override
@ -442,7 +444,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
if (toastSuccess) { if (toastSuccess) {
// Open the app list after a successful pairing attempt // Open the app list after a successful pairing attempt
doAppList(computer, true); doAppList(computer, true, false);
} }
else { else {
// Start polling again if we're still in the foreground // Start polling again if we're still in the foreground
@ -541,7 +543,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
}).start(); }).start();
} }
private void doAppList(ComputerDetails computer, boolean newlyPaired) { private void doAppList(ComputerDetails computer, boolean newlyPaired, boolean showHiddenGames) {
if (computer.state == ComputerDetails.State.OFFLINE) { if (computer.state == ComputerDetails.State.OFFLINE) {
Toast.makeText(PcView.this, getResources().getString(R.string.error_pc_offline), Toast.LENGTH_SHORT).show(); Toast.makeText(PcView.this, getResources().getString(R.string.error_pc_offline), Toast.LENGTH_SHORT).show();
return; return;
@ -555,6 +557,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
i.putExtra(AppView.NAME_EXTRA, computer.name); i.putExtra(AppView.NAME_EXTRA, computer.name);
i.putExtra(AppView.UUID_EXTRA, computer.uuid); i.putExtra(AppView.UUID_EXTRA, computer.uuid);
i.putExtra(AppView.NEW_PAIR_EXTRA, newlyPaired); i.putExtra(AppView.NEW_PAIR_EXTRA, newlyPaired);
i.putExtra(AppView.SHOW_HIDDEN_APPS_EXTRA, showHiddenGames);
startActivity(i); startActivity(i);
} }
@ -592,8 +595,9 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
}, null); }, null);
return true; return true;
case FULL_APP_LIST_ID:
case APP_LIST_ID: case APP_LIST_ID:
doAppList(computer.details, false); doAppList(computer.details, false, item.getItemId() == FULL_APP_LIST_ID);
return true; return true;
case RESUME_ID: case RESUME_ID:
@ -635,6 +639,12 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
new DiskAssetLoader(this).deleteAssetsForComputer(details.uuid); 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++) { for (int i = 0; i < pcGridAdapter.getCount(); i++) {
ComputerObject computer = (ComputerObject) pcGridAdapter.getItem(i); ComputerObject computer = (ComputerObject) pcGridAdapter.getItem(i);
@ -711,7 +721,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
// Pair an unpaired machine by default // Pair an unpaired machine by default
doPair(computer.details); doPair(computer.details);
} else { } else {
doAppList(computer.details, false); doAppList(computer.details, false, false);
} }
} }
}); });

View File

@ -17,8 +17,12 @@ import com.limelight.grid.assets.NetworkAssetLoader;
import com.limelight.nvstream.http.ComputerDetails; import com.limelight.nvstream.http.ComputerDetails;
import com.limelight.preferences.PreferenceConfiguration; import com.limelight.preferences.PreferenceConfiguration;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> { public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
@ -28,18 +32,38 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
private final ComputerDetails computer; private final ComputerDetails computer;
private final String uniqueId; private final String uniqueId;
private final boolean showHiddenApps;
private CachedAppAssetLoader loader; private CachedAppAssetLoader loader;
private Set<Integer> hiddenAppIds = new HashSet<>();
private ArrayList<AppView.AppObject> 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)); super(context, getLayoutIdForPreferences(prefs));
this.computer = computer; this.computer = computer;
this.uniqueId = uniqueId; this.uniqueId = uniqueId;
this.showHiddenApps = showHiddenApps;
updateLayoutWithPreferences(context, prefs); updateLayoutWithPreferences(context, prefs);
} }
public void updateHiddenApps(Set<Integer> 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) { private static int getLayoutIdForPreferences(PreferenceConfiguration prefs) {
if (prefs.smallIconMode) { if (prefs.smallIconMode) {
return R.layout.app_grid_item_small; return R.layout.app_grid_item_small;
@ -88,8 +112,8 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
loader.freeCacheMemory(); loader.freeCacheMemory();
} }
private void sortList() { private static void sortList(List<AppView.AppObject> list) {
Collections.sort(itemList, new Comparator<AppView.AppObject>() { Collections.sort(list, new Comparator<AppView.AppObject>() {
@Override @Override
public int compare(AppView.AppObject lhs, AppView.AppObject rhs) { public int compare(AppView.AppObject lhs, AppView.AppObject rhs) {
return lhs.app.getAppName().toLowerCase().compareTo(rhs.app.getAppName().toLowerCase()); return lhs.app.getAppName().toLowerCase().compareTo(rhs.app.getAppName().toLowerCase());
@ -98,16 +122,33 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
} }
public void addApp(AppView.AppObject app) { public void addApp(AppView.AppObject app) {
// Queue a request to fetch this bitmap into cache // Update hidden state
loader.queueCacheLoad(app.app); app.isHidden = hiddenAppIds.contains(app.app.getAppId());
// Add the app to our sorted list // Always add the app to the all apps list
itemList.add(app); allApps.add(app);
sortList(); 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) { public void removeApp(AppView.AppObject app) {
itemList.remove(app); itemList.remove(app);
allApps.remove(app);
}
@Override
public void clear() {
super.clear();
allApps.clear();
} }
@Override @Override
@ -123,5 +164,12 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
else { else {
overlayView.setVisibility(View.GONE); overlayView.setVisibility(View.GONE);
} }
if (obj.isHidden) {
parentView.setAlpha(0.40f);
}
else {
parentView.setAlpha(1.0f);
}
} }
} }

View File

@ -15,7 +15,8 @@
<string name="help_loading_msg">Loading help page…</string> <string name="help_loading_msg">Loading help page…</string>
<!-- PC view menu entries --> <!-- PC view menu entries -->
<string name="pcview_menu_app_list">View Game List</string> <string name="pcview_menu_app_list">View Apps</string>
<string name="pcview_menu_full_app_list">View Hidden Apps</string>
<string name="pcview_menu_pair_pc">Pair with PC</string> <string name="pcview_menu_pair_pc">Pair with PC</string>
<string name="pcview_menu_unpair_pc">Unpair</string> <string name="pcview_menu_unpair_pc">Unpair</string>
<string name="pcview_menu_send_wol">Send Wake-On-LAN request</string> <string name="pcview_menu_send_wol">Send Wake-On-LAN request</string>
@ -99,6 +100,7 @@
<string name="applist_menu_details">View Details</string> <string name="applist_menu_details">View Details</string>
<string name="applist_menu_scut">Create Shortcut</string> <string name="applist_menu_scut">Create Shortcut</string>
<string name="applist_menu_tv_channel">Add to Channel</string> <string name="applist_menu_tv_channel">Add to Channel</string>
<string name="applist_menu_hide_app">Hide App</string>
<string name="applist_refresh_title">App List</string> <string name="applist_refresh_title">App List</string>
<string name="applist_refresh_msg">Refreshing apps…</string> <string name="applist_refresh_msg">Refreshing apps…</string>
<string name="applist_refresh_error_title">Error</string> <string name="applist_refresh_error_title">Error</string>