From b7b6adaff7fc414a99f502021babd9919f9ce6f0 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sat, 27 Jul 2024 17:23:23 -0400 Subject: [PATCH] feat(activity): allow PC Name and AppName for ShortcutTrampoline (#1387) --- .../com/limelight/ShortcutTrampoline.java | 132 +++++++++++++++--- .../computers/ComputerDatabaseManager.java | 29 ++++ .../com/limelight/nvstream/http/NvHTTP.java | 21 ++- .../com/limelight/utils/ShortcutHelper.java | 3 - 4 files changed, 155 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/limelight/ShortcutTrampoline.java b/app/src/main/java/com/limelight/ShortcutTrampoline.java index 777647b6..6157379e 100644 --- a/app/src/main/java/com/limelight/ShortcutTrampoline.java +++ b/app/src/main/java/com/limelight/ShortcutTrampoline.java @@ -8,19 +8,26 @@ import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; +import com.limelight.computers.ComputerDatabaseManager; import com.limelight.computers.ComputerManagerListener; import com.limelight.computers.ComputerManagerService; import com.limelight.nvstream.http.ComputerDetails; import com.limelight.nvstream.http.NvApp; +import com.limelight.nvstream.http.NvHTTP; import com.limelight.nvstream.http.PairingManager; import com.limelight.nvstream.wol.WakeOnLanSender; +import com.limelight.utils.CacheHelper; import com.limelight.utils.Dialog; import com.limelight.utils.ServerHelper; import com.limelight.utils.SpinnerDialog; import com.limelight.utils.UiHelper; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; +import java.io.StringReader; import java.util.ArrayList; +import java.util.List; import java.util.UUID; public class ShortcutTrampoline extends Activity { @@ -33,6 +40,7 @@ public class ShortcutTrampoline extends Activity { private SpinnerDialog blockingLoadSpinner; private ComputerManagerService.ComputerManagerBinder managerBinder; + private final ServiceConnection serviceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder binder) { final ComputerManagerService.ComputerManagerBinder localBinder = @@ -214,9 +222,9 @@ public class ShortcutTrampoline extends Activity { } }; - protected boolean validateInput(String uuidString, String appIdString) { - // Validate UUID - if (uuidString == null) { + protected boolean validateInput(String uuidString, String appIdString, String nameString) { + // Validate PC UUID/Name + if (uuidString == null && nameString == null) { Dialog.displayDialog(ShortcutTrampoline.this, getResources().getString(R.string.conn_error_title), getResources().getString(R.string.scut_invalid_uuid), @@ -224,14 +232,25 @@ public class ShortcutTrampoline extends Activity { return false; } - try { - UUID.fromString(uuidString); - } catch (IllegalArgumentException ex) { - Dialog.displayDialog(ShortcutTrampoline.this, - getResources().getString(R.string.conn_error_title), - getResources().getString(R.string.scut_invalid_uuid), - true); - return false; + if (uuidString != null && !uuidString.isEmpty()) { + try { + UUID.fromString(uuidString); + } catch (IllegalArgumentException ex) { + Dialog.displayDialog(ShortcutTrampoline.this, + getResources().getString(R.string.conn_error_title), + getResources().getString(R.string.scut_invalid_uuid), + true); + return false; + } + } else { + // UUID is null, so fallback to Name + if (nameString == null || nameString.isEmpty()) { + Dialog.displayDialog(ShortcutTrampoline.this, + getResources().getString(R.string.conn_error_title), + getResources().getString(R.string.scut_invalid_uuid), + true); + return false; + } } // Validate App ID (if provided) @@ -255,24 +274,93 @@ public class ShortcutTrampoline extends Activity { super.onCreate(savedInstanceState); UiHelper.notifyNewRootView(this); + ComputerDatabaseManager dbManager = new ComputerDatabaseManager(this); + ComputerDetails _computer = null; - String appIdString = getIntent().getStringExtra(Game.EXTRA_APP_ID); + // PC arguments, both are optional, but at least one must be provided uuidString = getIntent().getStringExtra(AppView.UUID_EXTRA); + String nameString = getIntent().getStringExtra(AppView.NAME_EXTRA); - if (validateInput(uuidString, appIdString)) { - if (appIdString != null && !appIdString.isEmpty()) { - app = new NvApp(getIntent().getStringExtra(Game.EXTRA_APP_NAME), - Integer.parseInt(appIdString), - getIntent().getBooleanExtra(Game.EXTRA_APP_HDR, false)); + // App arguments, both are optional, but one must be provided in order to start an app + String appIdString = getIntent().getStringExtra(Game.EXTRA_APP_ID); + String appNameString = getIntent().getStringExtra(Game.EXTRA_APP_NAME); + + if (!validateInput(uuidString, appIdString, nameString)) { + // Invalid input, so just return + return; + } + + if (uuidString == null || uuidString.isEmpty()) { + // Use nameString to find the corresponding UUID + _computer = dbManager.getComputerByName(nameString); + + if (_computer == null) { + Dialog.displayDialog(ShortcutTrampoline.this, + getResources().getString(R.string.conn_error_title), + getResources().getString(R.string.scut_pc_not_found), + true); + return; } - // Bind to the computer manager service - bindService(new Intent(this, ComputerManagerService.class), serviceConnection, - Service.BIND_AUTO_CREATE); + uuidString = _computer.uuid; - blockingLoadSpinner = SpinnerDialog.displayDialog(this, getResources().getString(R.string.conn_establishing_title), - getResources().getString(R.string.applist_connect_msg), true); + // Set the AppView UUID intent, since it wasn't provided + setIntent(new Intent(getIntent()).putExtra(AppView.UUID_EXTRA, uuidString)); } + + if (appIdString != null && !appIdString.isEmpty()) { + app = new NvApp(getIntent().getStringExtra(Game.EXTRA_APP_NAME), + Integer.parseInt(appIdString), + getIntent().getBooleanExtra(Game.EXTRA_APP_HDR, false)); + } + else if (appNameString != null && !appNameString.isEmpty()) { + // Use appNameString to find the corresponding AppId + try { + int appId = -1; + String rawAppList = CacheHelper.readInputStreamToString(CacheHelper.openCacheFileForInput(getCacheDir(), "applist", uuidString)); + + if (rawAppList.isEmpty()) { + Dialog.displayDialog(ShortcutTrampoline.this, + getResources().getString(R.string.conn_error_title), + getResources().getString(R.string.scut_invalid_app_id), + true); + return; + } + List applist = NvHTTP.getAppListByReader(new StringReader(rawAppList)); + + for (NvApp _app : applist) { + if (_app.getAppName().equals(appNameString)) { + appId = _app.getAppId(); + break; + } + } + if (appId < 0) { + Dialog.displayDialog(ShortcutTrampoline.this, + getResources().getString(R.string.conn_error_title), + getResources().getString(R.string.scut_invalid_app_id), + true); + return; + } + setIntent(new Intent(getIntent()).putExtra(Game.EXTRA_APP_ID, appId)); + app = new NvApp( + appNameString, + appId, + getIntent().getBooleanExtra(Game.EXTRA_APP_HDR, false)); + } catch (IOException | XmlPullParserException e) { + Dialog.displayDialog(ShortcutTrampoline.this, + getResources().getString(R.string.conn_error_title), + getResources().getString(R.string.scut_invalid_app_id), + true); + return; + } + } + + // Bind to the computer manager service + bindService(new Intent(this, ComputerManagerService.class), serviceConnection, + Service.BIND_AUTO_CREATE); + + blockingLoadSpinner = SpinnerDialog.displayDialog(this, getResources().getString(R.string.conn_establishing_title), + getResources().getString(R.string.applist_connect_msg), true); } @Override diff --git a/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java b/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java index 54e656af..692c433f 100644 --- a/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java +++ b/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java @@ -190,6 +190,35 @@ public class ComputerDatabaseManager { } } + /** + * Get a computer by name + * NOTE: It is perfectly valid for multiple computers to have the same name, + * this function will only return the first one it finds. + * Consider using getComputerByUUID instead. + * @param name The name of the computer + * @see ComputerDatabaseManager#getComputerByUUID(String) for alternative. + * @return The computer details, or null if no computer with that name exists + */ + public ComputerDetails getComputerByName(String name) { + try (final Cursor c = computerDb.query( + COMPUTER_TABLE_NAME, null, COMPUTER_NAME_COLUMN_NAME+"=?", + new String[]{ name }, null, null, null) + ) { + if (!c.moveToFirst()) { + // No matching computer + return null; + } + + return getComputerFromCursor(c); + } + } + + /** + * Get a computer by UUID + * @param uuid The UUID of the computer + * @see ComputerDatabaseManager#getComputerByName(String) for alternative. + * @return The computer details, or null if no computer with that UUID exists + */ public ComputerDetails getComputerByUUID(String uuid) { try (final Cursor c = computerDb.query( COMPUTER_TABLE_NAME, null, COMPUTER_UUID_COLUMN_NAME+"=?", diff --git a/app/src/main/java/com/limelight/nvstream/http/NvHTTP.java b/app/src/main/java/com/limelight/nvstream/http/NvHTTP.java index c4dcb537..13dad8be 100644 --- a/app/src/main/java/com/limelight/nvstream/http/NvHTTP.java +++ b/app/src/main/java/com/limelight/nvstream/http/NvHTTP.java @@ -586,6 +586,12 @@ public class NvHTTP { } } + /** + * Get an app by ID + * @param appId The ID of the app + * @see #getAppByName(String) for alternative. + * @return app details, or null if no app with that ID exists + */ public NvApp getAppById(int appId) throws IOException, XmlPullParserException { LinkedList appList = getAppList(); for (NvApp appFromList : appList) { @@ -595,11 +601,16 @@ public class NvHTTP { } return null; } - - /* NOTE: Only use this function if you know what you're doing. - * It's totally valid to have two apps named the same thing, - * or even nothing at all! Look apps up by ID if at all possible - * using the above function */ + + /** + * Get an app by name + * NOTE: It is perfectly valid for multiple apps to have the same name, + * this function will only return the first one it finds. + * Consider using getAppById instead. + * @param appName The name of the app + * @see #getAppById(int) for alternative. + * @return app details, or null if no app with that name exists + */ public NvApp getAppByName(String appName) throws IOException, XmlPullParserException { LinkedList appList = getAppList(); for (NvApp appFromList : appList) { diff --git a/app/src/main/java/com/limelight/utils/ShortcutHelper.java b/app/src/main/java/com/limelight/utils/ShortcutHelper.java index 07995847..759ac930 100644 --- a/app/src/main/java/com/limelight/utils/ShortcutHelper.java +++ b/app/src/main/java/com/limelight/utils/ShortcutHelper.java @@ -2,15 +2,12 @@ package com.limelight.utils; import android.annotation.TargetApi; import android.app.Activity; -import android.content.Intent; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.os.Build; -import com.limelight.AppView; -import com.limelight.ShortcutTrampoline; import com.limelight.R; import com.limelight.nvstream.http.ComputerDetails; import com.limelight.nvstream.http.NvApp;