Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/launcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ tools/tv.py 'settings put secure screensaver_components com.gathin.tvhome/.Clock
- A `hold:` prefix on an `app_keys` entry gives that button a second app: a normal press opens the
unprefixed app, holding for half a second opens the hold one (by keycode or `hold:scan:...`). An entry can
name an activity (`package/.Activity`), such as an add-on's quick bar, instead of the app's launcher entry.
A `front:` prefix sets what a tap opens while that button's own app is already in front (`HomeKeyService`
only).
A `front:` prefix sets what a tap opens while that button's own app is already in front. It needs
`HomeKeyService` bound (`HOME_KEY_SERVICE=1`); `InputKeyService` follows it then.
- In place of an app, an `app_keys` entry can name an action: `back`, `home`, `recents`, `mute`, `sleep`
(standby, Android 9 and later), `screensaver`, `settings`, `wifi`, `library` (Home with the App library
open, for a remote's Apps button; needs `app_library`) or `ignore` (the button does nothing), so a spare
Expand Down
10 changes: 4 additions & 6 deletions launcher/app/src/main/java/com/gathin/tvhome/HomeKeyService.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,9 @@ public class HomeKeyService extends AccessibilityService {
private long powerOnUntil;
/** home_long_press_home: this service takes every Home press, held ones included. */
private boolean homeLongPressHome;
/** app_keys "front:" entries, and the package of the activity in front, from window changes while there are any. */
/** app_keys "front:" entries; the package in front goes to RemoteKeys.frontPackage, for both helpers. */
private Map<Integer, String> frontAppKeys = Collections.emptyMap();
private Map<Integer, String> frontAppScancodes = Collections.emptyMap();
private String frontPackage = "";
/** power_on_home_always: Home at every power-on, not only over Live TV. */
private boolean powerOnAlways;
/** power_on_home: re-arms after the screen turns on; null unless the key is on. */
Expand Down Expand Up @@ -139,6 +138,7 @@ static void applyStandbyOptIn() {
@Override
public void onDestroy() {
if (running == this) running = null;
RemoteKeys.frontPackage = "";
RemoteKeys.release(this);
stopWaitingForUnlock();
if (screenOnReceiver != null) unregisterReceiver(screenOnReceiver);
Expand Down Expand Up @@ -242,9 +242,7 @@ private void loadConfig() {

/** A tap's target: the "front:" entry while the button's own app is already in front, else the usual one. */
private String inFront(Map<Integer, String> keys, Map<Integer, String> front, int code) {
String target = keys.get(code);
String alt = front.get(code);
return alt != null && RemoteKeys.inFront(target, frontPackage) ? alt : target;
return RemoteKeys.tap(keys.get(code), front.get(code));
}

private boolean isSettingsKey(KeyEvent event) {
Expand Down Expand Up @@ -314,7 +312,7 @@ public void onAccessibilityEvent(AccessibilityEvent event) {
try {
ComponentName window = new ComponentName(event.getPackageName().toString(), event.getClassName().toString());
getPackageManager().getActivityInfo(window, 0);
frontPackage = window.getPackageName();
RemoteKeys.frontPackage = window.getPackageName();
} catch (PackageManager.NameNotFoundException notAnActivity) {
// a dialog, panel or overlay: the app under it is still in front
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class InputKeyService extends AccessibilityService {
private Map<Integer, String> appScancodes = Collections.emptyMap();
private Map<Integer, String> holdAppKeys = Collections.emptyMap();
private Map<Integer, String> holdAppScancodes = Collections.emptyMap();
/** app_keys "front:" entries, chosen the way HomeKeyService chooses them (RemoteKeys.tap). */
private Map<Integer, String> frontAppKeys = Collections.emptyMap();
private Map<Integer, String> frontAppScancodes = Collections.emptyMap();
/** restore_key_services was on when the config was read: this helper is noted for ServiceRepair. */
private boolean repairRecord;
/** consume_volume_keys: Volume Up/Down go to VolumeKeys instead of on to the firmware. */
Expand All @@ -47,6 +50,8 @@ protected void onServiceConnected() {
RemoteKeys.applyKeyFilter(this, config.keyFilter);
holdAppKeys = RemoteKeys.parseHoldAppKeys(config.appKeys);
holdAppScancodes = RemoteKeys.parseHoldAppScancodes(config.appKeys);
frontAppKeys = RemoteKeys.parseFrontAppKeys(config.appKeys);
frontAppScancodes = RemoteKeys.parseFrontAppScancodes(config.appKeys);
// gamepad_passthrough, off by default. Only with key_filter on: off, the service never filters keys.
if (config.gamepadPassthrough && config.keyFilter && gamepads == null) {
gamepads = new GamepadPassthrough(this);
Expand All @@ -64,9 +69,11 @@ private boolean isInputKey(int keyCode) {
protected boolean onKeyEvent(KeyEvent event) {
if (consumeVolumeKeys && VolumeKeys.handle(this, event)) return true;
// A scancode entry is more specific than any keycode, so it goes first.
if (RemoteKeys.handle(this, event, appScancodes.get(event.getScanCode()),
if (RemoteKeys.handle(this, event, RemoteKeys.tap(appScancodes.get(event.getScanCode()),
frontAppScancodes.get(event.getScanCode())),
holdAppScancodes.get(event.getScanCode()))) return true;
if (RemoteKeys.handle(this, event, appKeys.get(event.getKeyCode()),
if (RemoteKeys.handle(this, event, RemoteKeys.tap(appKeys.get(event.getKeyCode()),
frontAppKeys.get(event.getKeyCode())),
holdAppKeys.get(event.getKeyCode()))) return true;
if (!isInputKey(event.getKeyCode())) return false;
if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()) {
Expand Down
8 changes: 8 additions & 0 deletions launcher/app/src/main/java/com/gathin/tvhome/RemoteKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ static boolean inFront(String target, String front) {
return (slash < 0 ? target : target.substring(0, slash)).equals(front);
}

/** The app in front, from HomeKeyService's window changes while app_keys has "front:" entries; "" otherwise. */
static String frontPackage = "";

/** A tap's target for both helpers: its "front:" entry while the button's own app is in front, else the usual one. */
static String tap(String target, String alt) {
return alt != null && inFront(target, frontPackage) ? alt : target;
}

/** Created on first use: building a Handler while the class loads breaks the JVM tests. */
private static Presses presses;

Expand Down
17 changes: 17 additions & 0 deletions launcher/app/src/test/java/com/gathin/tvhome/RemoteKeysTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ private static Map<Integer, String> parseHold(String... entries) {
assertFalse(RemoteKeys.inFront("input:com.vt.source.external/.HdmiInputService/HW2", "com.vt.source.external"));
}

// Both helpers pick a tap's target here, so InputKeyService cannot open the usual app over a "front:" entry.
@Test public void tapPicksTheFrontEntryOnlyWhileTheButtonsAppIsInFront() {
String before = RemoteKeys.frontPackage;
try {
RemoteKeys.frontPackage = "com.vt.tv.tvcenter";
assertEquals("guide:com.vt.tv.tvcenter", RemoteKeys.tap("com.vt.tv.tvcenter", "guide:com.vt.tv.tvcenter"));
assertEquals("com.vt.tv.tvcenter", RemoteKeys.tap("com.vt.tv.tvcenter", null));
RemoteKeys.frontPackage = "com.gathin.tvhome";
assertEquals("com.vt.tv.tvcenter", RemoteKeys.tap("com.vt.tv.tvcenter", "guide:com.vt.tv.tvcenter"));
RemoteKeys.frontPackage = "";
assertEquals("com.vt.tv.tvcenter", RemoteKeys.tap("com.vt.tv.tvcenter", "guide:com.vt.tv.tvcenter"));
assertNull(RemoteKeys.tap(null, "guide:com.vt.tv.tvcenter"));
} finally {
RemoteKeys.frontPackage = before;
}
}

@Test public void frontEntriesAreReadApartFromTheRest() {
List<String> entries = Arrays.asList("172:com.vt.tv.tvcenter", "front:172:guide:com.vt.tv.tvcenter",
"front:scan:490:com.wbd.hbomax", "hold:172:a.b");
Expand Down
Loading