From d54fdc9f5ff066bfffea5c2bb31f8d3a7c1e9177 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 16 Jul 2019 22:16:29 -0700 Subject: [PATCH] Refactor shortcut and channel code and handle removal of apps and PCs properly --- app/src/main/java/com/limelight/AppView.java | 13 +-- app/src/main/java/com/limelight/Game.java | 11 ++- app/src/main/java/com/limelight/PcView.java | 2 +- .../com/limelight/utils/ShortcutHelper.java | 81 ++++++++++++------- .../com/limelight/utils/TvChannelHelper.java | 67 ++++++++++----- 5 files changed, 113 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/com/limelight/AppView.java b/app/src/main/java/com/limelight/AppView.java index 17112cf7..ce5e85c3 100644 --- a/app/src/main/java/com/limelight/AppView.java +++ b/app/src/main/java/com/limelight/AppView.java @@ -90,6 +90,10 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { return; } + // Add a launcher shortcut for this PC (forced, since this is user interaction) + shortcutHelper.createAppViewShortcut(computer, true, getIntent().getBooleanExtra(NEW_PAIR_EXTRA, false)); + shortcutHelper.reportComputerShortcutUsed(computer); + try { appGridAdapter = new AppGridAdapter(AppView.this, PreferenceConfiguration.readPreferences(AppView.this).listMode, @@ -182,7 +186,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { @Override public void run() { // Disable shortcuts referencing this PC for now - shortcutHelper.disableShortcut(details.uuid, + shortcutHelper.disableComputerShortcut(details, getResources().getString(R.string.scut_not_paired)); // Display a toast to the user and quit the activity @@ -268,10 +272,6 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { setTitle(computerName); label.setText(computerName); - // Add a launcher shortcut for this PC (forced, since this is user interaction) - shortcutHelper.createAppViewShortcut(uuidString, computerName, uuidString, true, getIntent().getBooleanExtra(NEW_PAIR_EXTRA, false)); - shortcutHelper.reportShortcutUsed(uuidString); - // Bind to the computer manager service bindService(new Intent(this, ComputerManagerService.class), serviceConnection, Service.BIND_AUTO_CREATE); @@ -420,7 +420,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { case CREATE_SHORTCUT_ID: ImageView appImageView = info.targetView.findViewById(R.id.grid_image); Bitmap appBits = ((BitmapDrawable)appImageView.getDrawable()).getBitmap(); - if (!shortcutHelper.createPinnedGameShortcut(uuidString + Integer.valueOf(app.app.getAppId()).toString(), appBits, computer, app.app)) { + if (!shortcutHelper.createPinnedGameShortcut(computer, app.app, appBits)) { Toast.makeText(AppView.this, getResources().getString(R.string.unable_to_pin_shortcut), Toast.LENGTH_LONG).show(); } return true; @@ -516,6 +516,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { // This app was removed in the latest app list if (!foundExistingApp) { + shortcutHelper.disableAppShortcut(computer, existingApp.app, "App removed from PC"); appGridAdapter.removeApp(existingApp); updated = true; diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index ddb68c07..894ef479 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -17,6 +17,7 @@ import com.limelight.binding.video.PerfOverlayListener; import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnectionListener; import com.limelight.nvstream.StreamConfiguration; +import com.limelight.nvstream.http.ComputerDetails; import com.limelight.nvstream.http.NvApp; import com.limelight.nvstream.input.KeyboardPacket; import com.limelight.nvstream.input.MouseButtonPacket; @@ -269,10 +270,16 @@ public class Game extends Activity implements SurfaceHolder.Callback, } // Report this shortcut being used + ComputerDetails computer = new ComputerDetails(); + computer.name = pcName; + computer.uuid = uuid; shortcutHelper = new ShortcutHelper(this); - shortcutHelper.reportShortcutUsed(uuid); + shortcutHelper.reportComputerShortcutUsed(computer); if (appName != null) { - shortcutHelper.reportGameLaunched(uuid, pcName, ""+appId, appName); + NvApp app = new NvApp(); + app.setAppId(appId); + app.setAppName(appName); + shortcutHelper.reportGameLaunched(computer, app); } // Initialize the MediaCodec helper before creating the decoder diff --git a/app/src/main/java/com/limelight/PcView.java b/app/src/main/java/com/limelight/PcView.java index a0f81a73..5e27e343 100644 --- a/app/src/main/java/com/limelight/PcView.java +++ b/app/src/main/java/com/limelight/PcView.java @@ -636,7 +636,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { if (details.equals(computer.details)) { // Disable or delete shortcuts referencing this PC - shortcutHelper.disableShortcut(details.uuid, + shortcutHelper.disableComputerShortcut(details, getResources().getString(R.string.scut_deleted_pc)); pcGridAdapter.removeComputer(computer); diff --git a/app/src/main/java/com/limelight/utils/ShortcutHelper.java b/app/src/main/java/com/limelight/utils/ShortcutHelper.java index 71d9b4c7..221e9b6a 100644 --- a/app/src/main/java/com/limelight/utils/ShortcutHelper.java +++ b/app/src/main/java/com/limelight/utils/ShortcutHelper.java @@ -82,44 +82,44 @@ public class ShortcutHelper { return false; } - public void reportShortcutUsed(String computerUuid) { + public void reportComputerShortcutUsed(ComputerDetails computer) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - if (getInfoForId(computerUuid) != null) { - sm.reportShortcutUsed(computerUuid); + if (getInfoForId(computer.uuid) != null) { + sm.reportShortcutUsed(computer.uuid); } } } - public void reportGameLaunched(String computerUuid, String computerName, String appId, String appName) { - tvChannelHelper.createTvChannel(computerUuid, computerName); - tvChannelHelper.addGameToChannel(computerUuid, computerName, appId, appName); + public void reportGameLaunched(ComputerDetails computer, NvApp app) { + tvChannelHelper.createTvChannel(computer); + tvChannelHelper.addGameToChannel(computer, app); } - public void createAppViewShortcut(String id, String computerName, String computerUuid, boolean forceAdd, boolean newlyPaired) { + public void createAppViewShortcut(ComputerDetails computer, boolean forceAdd, boolean newlyPaired) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { Intent i = new Intent(context, ShortcutTrampoline.class); - i.putExtra(AppView.NAME_EXTRA, computerName); - i.putExtra(AppView.UUID_EXTRA, computerUuid); + i.putExtra(AppView.NAME_EXTRA, computer.name); + i.putExtra(AppView.UUID_EXTRA, computer.uuid); i.setAction(Intent.ACTION_DEFAULT); - ShortcutInfo sinfo = new ShortcutInfo.Builder(context, id) + ShortcutInfo sinfo = new ShortcutInfo.Builder(context, computer.uuid) .setIntent(i) - .setShortLabel(computerName) - .setLongLabel(computerName) + .setShortLabel(computer.name) + .setLongLabel(computer.name) .setIcon(Icon.createWithResource(context, R.mipmap.ic_pc_scut)) .build(); - ShortcutInfo existingSinfo = getInfoForId(id); + ShortcutInfo existingSinfo = getInfoForId(computer.uuid); if (existingSinfo != null) { // Update in place sm.updateShortcuts(Collections.singletonList(sinfo)); - sm.enableShortcuts(Collections.singletonList(id)); + sm.enableShortcuts(Collections.singletonList(computer.uuid)); } // Reap shortcuts to make space for this if it's new // NOTE: This CAN'T be an else on the above if, because it's // possible that we have an existing shortcut but it's not a dynamic one. - if (!isExistingDynamicShortcut(id)) { + if (!isExistingDynamicShortcut(computer.uuid)) { // To avoid a random carousel of shortcuts popping in and out based on polling status, // we only add shortcuts if it's not at the limit or the user made a conscious action // to interact with this PC. @@ -132,24 +132,28 @@ public class ShortcutHelper { if (newlyPaired) { // Avoid hammering the channel API for each computer poll because it will throttle us - tvChannelHelper.createTvChannel(computerUuid, computerName); - tvChannelHelper.requestChannelOnHomeScreen(computerUuid); + tvChannelHelper.createTvChannel(computer); + tvChannelHelper.requestChannelOnHomeScreen(computer); } } public void createAppViewShortcutForOnlineHost(ComputerDetails details) { - createAppViewShortcut(details.uuid, details.name, details.uuid, false, false); + createAppViewShortcut(details, false, false); + } + + private String getShortcutIdForGame(ComputerDetails computer, NvApp app) { + return computer.uuid + app.getAppId(); } @TargetApi(Build.VERSION_CODES.O) - public boolean createPinnedGameShortcut(String id, Bitmap iconBits, String computerName, String computerUuid, String appName, String appId) { + public boolean createPinnedGameShortcut(ComputerDetails computer, NvApp app, Bitmap iconBits) { if (sm.isRequestPinShortcutSupported()) { Icon appIcon; Intent i = new Intent(context, ShortcutTrampoline.class); - i.putExtra(AppView.NAME_EXTRA, computerName); - i.putExtra(AppView.UUID_EXTRA, computerUuid); - i.putExtra(ShortcutTrampoline.APP_ID_EXTRA, appId); + i.putExtra(AppView.NAME_EXTRA, computer.name); + i.putExtra(AppView.UUID_EXTRA, computer.uuid); + i.putExtra(ShortcutTrampoline.APP_ID_EXTRA, ""+app.getAppId()); i.setAction(Intent.ACTION_DEFAULT); if (iconBits != null) { @@ -158,9 +162,9 @@ public class ShortcutHelper { appIcon = Icon.createWithResource(context, R.mipmap.ic_pc_scut); } - ShortcutInfo sInfo = new ShortcutInfo.Builder(context, id) + ShortcutInfo sInfo = new ShortcutInfo.Builder(context, getShortcutIdForGame(computer, app)) .setIntent(i) - .setShortLabel(appName + " (" + computerName + ")") + .setShortLabel(app.getAppName() + " (" + computer.name + ")") .setIcon(appIcon) .build(); @@ -170,15 +174,32 @@ public class ShortcutHelper { } } - public boolean createPinnedGameShortcut(String id, Bitmap iconBits, ComputerDetails cDetails, NvApp app) { - return createPinnedGameShortcut(id, iconBits, cDetails.name, cDetails.uuid, app.getAppName(), Integer.valueOf(app.getAppId()).toString()); + public void disableComputerShortcut(ComputerDetails computer, CharSequence reason) { + tvChannelHelper.deleteChannel(computer); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { + // Delete the computer shortcut itself + if (getInfoForId(computer.uuid) != null) { + sm.disableShortcuts(Collections.singletonList(computer.uuid), reason); + } + + // Delete all associated app shortcuts too + List shortcuts = getAllShortcuts(); + LinkedList appShortcutIds = new LinkedList<>(); + for (ShortcutInfo info : shortcuts) { + if (info.getId().startsWith(computer.uuid)) { + appShortcutIds.add(info.getId()); + } + } + sm.disableShortcuts(appShortcutIds, reason); + } } - public void disableShortcut(String uuid, CharSequence reason) { - tvChannelHelper.deleteChannel(uuid); + public void disableAppShortcut(ComputerDetails computer, NvApp app, CharSequence reason) { + tvChannelHelper.deleteProgram(computer, app); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - if (getInfoForId(uuid) != null) { - sm.disableShortcuts(Collections.singletonList(uuid), reason); + String id = getShortcutIdForGame(computer, app); + if (getInfoForId(id) != null) { + sm.disableShortcuts(Collections.singletonList(id), reason); } } } diff --git a/app/src/main/java/com/limelight/utils/TvChannelHelper.java b/app/src/main/java/com/limelight/utils/TvChannelHelper.java index 995ee77b..b3fa7725 100644 --- a/app/src/main/java/com/limelight/utils/TvChannelHelper.java +++ b/app/src/main/java/com/limelight/utils/TvChannelHelper.java @@ -21,6 +21,8 @@ import com.limelight.LimeLog; import com.limelight.PosterContentProvider; import com.limelight.R; import com.limelight.ShortcutTrampoline; +import com.limelight.nvstream.http.ComputerDetails; +import com.limelight.nvstream.http.NvApp; import java.io.IOException; import java.io.OutputStream; @@ -37,19 +39,19 @@ public class TvChannelHelper { this.context = context; } - void requestChannelOnHomeScreen(String computerUuid) { + void requestChannelOnHomeScreen(ComputerDetails computer) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!isAndroidTV()) { return; } - Long channelId = getChannelId(computerUuid); + Long channelId = getChannelId(computer.uuid); if (channelId == null) { return; } Intent intent = new Intent(TvContract.ACTION_REQUEST_CHANNEL_BROWSABLE); - intent.putExtra(TvContract.EXTRA_CHANNEL_ID, getChannelId(computerUuid)); + intent.putExtra(TvContract.EXTRA_CHANNEL_ID, getChannelId(computer.uuid)); try { context.startActivityForResult(intent, 0); } catch (ActivityNotFoundException e) { @@ -57,23 +59,23 @@ public class TvChannelHelper { } } - void createTvChannel(String computerUuid, String computerName) { + void createTvChannel(ComputerDetails computer) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!isAndroidTV()) { return; } Intent i = new Intent(context, ShortcutTrampoline.class); - i.putExtra(AppView.NAME_EXTRA, computerName); - i.putExtra(AppView.UUID_EXTRA, computerUuid); + i.putExtra(AppView.NAME_EXTRA, computer.name); + i.putExtra(AppView.UUID_EXTRA, computer.uuid); i.setAction(Intent.ACTION_DEFAULT); ChannelBuilder builder = new ChannelBuilder() .setType(TvContract.Channels.TYPE_PREVIEW) - .setDisplayName(computerName) - .setInternalProviderId(computerUuid) + .setDisplayName(computer.name) + .setInternalProviderId(computer.uuid) .setAppLinkIntent(i); - Long channelId = getChannelId(computerUuid); + Long channelId = getChannelId(computer.uuid); if (channelId != null) { context.getContentResolver().update(TvContract.buildChannelUri(channelId), builder.toContentValues(), null, null); @@ -117,7 +119,7 @@ public class TvChannelHelper { return bitmap; } - void addGameToChannel(String computerUuid, String computerName, String appId, String appName) { + void addGameToChannel(ComputerDetails computer, NvApp app) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!isAndroidTV()) { return; @@ -126,29 +128,29 @@ public class TvChannelHelper { PreviewProgramBuilder builder = new PreviewProgramBuilder(); Intent i = new Intent(context, ShortcutTrampoline.class); - i.putExtra(AppView.NAME_EXTRA, computerName); - i.putExtra(AppView.UUID_EXTRA, computerUuid); - i.putExtra(ShortcutTrampoline.APP_ID_EXTRA, appId); + i.putExtra(AppView.NAME_EXTRA, computer.name); + i.putExtra(AppView.UUID_EXTRA, computer.uuid); + i.putExtra(ShortcutTrampoline.APP_ID_EXTRA, ""+app.getAppId()); i.setAction(Intent.ACTION_DEFAULT); - Uri resourceURI = PosterContentProvider.createBoxArtUri(computerUuid, appId); + Uri resourceURI = PosterContentProvider.createBoxArtUri(computer.uuid, ""+app.getAppId()); - Long channelId = getChannelId(computerUuid); + Long channelId = getChannelId(computer.uuid); if (channelId == null) { return; } builder.setChannelId(channelId) .setType(TYPE_GAME) - .setTitle(appName) + .setTitle(app.getAppName()) .setPosterArtAspectRatio(ASPECT_RATIO_MOVIE_POSTER) .setPosterArtUri(resourceURI) .setIntent(i) - .setInternalProviderId(appId) + .setInternalProviderId(""+app.getAppId()) // Weight should increase each time we run the game .setWeight((int)((System.currentTimeMillis() - 1500000000000L) / 1000)); - Long programId = getProgramId(channelId, appId); + Long programId = getProgramId(channelId, ""+app.getAppId()); if (programId != null) { context.getContentResolver().update(TvContract.buildPreviewProgramUri(programId), builder.toContentValues(), null, null); @@ -162,18 +164,39 @@ public class TvChannelHelper { } } - void deleteChannel(String computerUuid) { + void deleteChannel(ComputerDetails computer) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!isAndroidTV()) { return; } - Long channelId = getChannelId(computerUuid); + Long channelId = getChannelId(computer.uuid); if (channelId == null) { return; } - Uri uri = TvContract.buildChannelUri(channelId); - context.getContentResolver().delete(uri, null, null); + + context.getContentResolver().delete(TvContract.buildChannelUri(channelId), null, null); + } + } + + void deleteProgram(ComputerDetails computer, NvApp app) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (!isAndroidTV()) { + return; + } + + Long channelId = getChannelId(computer.uuid); + if (channelId == null) { + return; + } + + + Long programId = getProgramId(channelId, ""+app.getAppId()); + if (programId == null) { + return; + } + + context.getContentResolver().delete(TvContract.buildPreviewProgramUri(programId), null, null); } }