Launcher: regression-test configured top-bar launch actions

This commit is contained in:
bearylabs committed 2026-10-25 17:24:45 +01:00
1 parent c1e2ea36cb
commit f07b564b2e
5 files changed
+85 -8

No files matched your search

@@ -0,0 +1,17 @@
package com.gathin.tvhome;
import java.util.Map;
import java.util.function.BiFunction;
/** Selects a configured, package-scoped action only when it resolves; otherwise keeps the normal launch. */
final class LaunchActions {
private LaunchActions() {}
static <T> T select(String pkg, Map<String, String> actions, T fallback,
BiFunction<String, String, T> resolver) {
String action = actions.get(pkg);
if (action == null) return fallback;
T custom = resolver.apply(pkg, action);
return custom != null ? custom : fallback;
}
}
@@ -2302,13 +2302,11 @@ public class MainActivity extends Activity implements AppAdapter.TileListener {
Intent launch = pm.getLeanbackLaunchIntentForPackage(pkg);
if (launch == null) launch = pm.getLaunchIntentForPackage(pkg);
if (launch == null) continue; // not installed, or disabled
// Honor the same configured actions as tiles. On Fire TV the default
// Appstore launch opens the app grid; FEATURED_LAUNCH opens the actual store.
String action = launchActions.get(pkg);
if (action != null) {
Intent custom = new Intent(action).setPackage(pkg);
if (pm.resolveActivity(custom, 0) != null) launch = custom;
}
// Use the same configured launch_actions as tiles; retain the normal entry if unavailable.
launch = LaunchActions.select(pkg, launchActions, launch, (targetPackage, action) -> {
Intent custom = new Intent(action).setPackage(targetPackage);
return pm.resolveActivity(custom, 0) != null ? custom : null;
});
Drawable logo;
CharSequence label;
try {
@@ -0,0 +1,36 @@
package com.gathin.tvhome;
import java.util.Map;
import org.junit.Test;
import static org.junit.Assert.*;
public class LaunchActionsTest {
@Test public void configuredActionOverridesDefaultOnlyForItsPackage() {
Object normal = new Object(), featured = new Object();
Object result = LaunchActions.select("store", Map.of("store", "FEATURED_LAUNCH"), normal,
(pkg, action) -> {
assertEquals("store", pkg);
assertEquals("FEATURED_LAUNCH", action);
return featured;
});
assertSame(featured, result);
}
@Test public void unresolvedActionRetainsNormalEntry() {
Object normal = new Object();
assertSame(normal, LaunchActions.select("store", Map.of("store", "MISSING"), normal,
(pkg, action) -> null));
}
@Test public void unconfiguredPackageDoesNotTryOtherAppsAction() {
Object normal = new Object();
assertSame(normal, LaunchActions.select("other", Map.of("store", "FEATURED_LAUNCH"), normal,
(pkg, action) -> { throw new AssertionError("No action configured for this package"); }));
}
@Test public void emptyConfigurationRetainsNormalEntryWithoutResolving() {
Object normal = new Object();
assertSame(normal, LaunchActions.select("store", Map.of(), normal,
(pkg, action) -> { throw new AssertionError("Unnecessary resolution"); }));
}
}
+26
View File
@@ -0,0 +1,26 @@
"""UI wiring for the policy exercised by LaunchActionsTest on the JVM; no device access."""
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
JAVA = ROOT / 'launcher/app/src/main/java/com/gathin/tvhome'
class TopBarLaunchActionsTest(unittest.TestCase):
def setUp(self):
self.source = (JAVA / 'MainActivity.java').read_text()
self.method = self.source.split('private void addTopBarApps(', 1)[1].split(
'@Override public void onLibraryUp()', 1)[0]
def test_top_bar_passes_configured_actions_to_tested_selection_policy(self):
self.assertIn('addTopBarApps(cfg.topBarApps, cfg.launchActions)', self.source)
self.assertIn('LaunchActions.select(pkg, launchActions, launch,', self.method)
self.assertIn('button.setOnClickListener(v -> open(new Intent(target)))', self.method)
def test_action_is_package_scoped_resolved_and_does_not_replace_missing_apps(self):
self.assertIn('new Intent(action).setPackage(targetPackage)', self.method)
self.assertIn('pm.resolveActivity(custom, 0) != null ? custom : null', self.method)
self.assertLess(self.method.index('if (launch == null) continue'),
self.method.index('LaunchActions.select'))
self.assertIn('getLeanbackLaunchIntentForPackage(pkg)', self.method)
self.assertIn('getLaunchIntentForPackage(pkg)', self.method)
+1 -1
View File
@@ -49,7 +49,7 @@ A new key needs one there in the same pull request.
| `source_panel_service`, `source_panel_action` | string | The TV's own source panel, for sets with no TvInputManager: a service, or an activity (BYINTEK U4). Without a Live TV app, the Inputs pill opens it directly |
| `source_panel_extra` | `"name=value"` | One int extra sent with the source panel (Coocaa OS: `control_center_command=100`) |
| `coocaa_sources` | bool | Coocaa OS (Skyworth, China): list the sources from `com.skyworth.tv` in the Inputs menu, ahead of the source panel |
| `launch_actions` | list of `"package=action"` | Tiles that open a screen of the app instead of its start screen; an action that no longer resolves opens the app normally (MagentaTV's guide: `com.telekom.onetv.tv.pl=com.telekom.onetv.tv.pl.action.PROGRAM_GUIDE`) |
| `launch_actions` | list of `"package=action"` | Tiles and `top_bar_apps` buttons that open a screen of the app instead of its start screen; an action that no longer resolves opens the app normally (MagentaTV's guide: `com.telekom.onetv.tv.pl=com.telekom.onetv.tv.pl.action.PROGRAM_GUIDE`) |
| `app_labels` | list of `"package=Name"` | A tile's name instead of the app's own (MagentaTV as `com.telekom.onetv.tv.pl=TV`) |
| `app_banners` | list of `"package=banner_name"` | A tile's artwork from a `banner_*` drawable bundled with the launcher (`banner_tv`: magenta with the Live TV glyph) |
| `input_action` | string | Action sent with an HDMI/AV row's channel URI when nothing handles the standard `VIEW` (XGIMI: `com.xgimi.action.hdmiPlayer`). Only a handler-less set gets here; any app that claims the channel MIME type takes the row instead, so check with `query-activities` ([#805](https://github.com/Gathin23/cleanyourtv/issues/805)) |