Skip to content
Merged
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
3 changes: 2 additions & 1 deletion LABORATORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Two identical boxes with the same firmware (checked 2026-10-13):

| | Aceline-1 | Aceline-2 |
|---|---|---|
| Address | `172.17.32.144:5555` (Ethernet); Wi‑Fi is also up as `172.17.32.146` | `172.17.32.145:5555` (Ethernet) |
| Address | `172.17.32.144:5555` (Ethernet; Wi‑Fi switched off) | `172.17.32.145:5555` (Ethernet) |
| Display | **none** (headless): only `screencap`, the debug page and the logs show what is on screen | **monitor** Philips 226VL over HDMI (HDMI audio active); for checks that need a real picture or a person looking |
| Serial | `KFB442005119` | `KFB442001288` |
| Extra apps | Yandex Browser TV, Faceter, Record Radio | VK TV, Wink, VLC, Nova; autostart `com.oranth.atvapplist` |
Expand Down Expand Up @@ -254,6 +254,7 @@ The script uninstalls first. To test an upgrade that keeps the settings, use `$A
| UDP socket leak | `$ADB shell cat /proc/$(pidof com.openipc.decoder)/net/udp`, count lines before and after |
| CPU per thread | `$ADB shell top -H -p <pid>` (a spinning `rtsp-audio` thread was found this way) |
| Screen off / on | `input keyevent KEYCODE_SLEEP` / `KEYCODE_WAKEUP` |
| Menu with a remote (#91) | `input keyevent KEYCODE_DPAD_CENTER` opens the menu with the focus on the active item (slot, Q or U); `DPAD_RIGHT` ×n, `DPAD_DOWN` (Exit), `DPAD_UP`, then `DPAD_CENTER` must switch to that slot; `KEYCODE_BACK` closes it. The focused item: `uiautomator dump /sdcard/m.xml` and look for `focused="true"`. Check in single view and multi-view |
| 4:3 screen | `wm size 960x720`, back with `wm size reset` |
| Audio playback (sound cannot be heard remotely) | `dumpsys media.audio_flinger`: server position of the app's track; frames/s ≈ sample rate, no underruns |
| Screenshot | `$ADB exec-out screencap -p > shot.png`; long-press screenshot in the app saves to `Pictures/OpenIPC/` |
Expand Down
53 changes: 47 additions & 6 deletions android/app/src/main/java/com/openipc/decoder/MenuManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class MenuManager {
/** The UDP RTP view is shown: neither a slot nor multi-view is active. */
private boolean udpShown;
private boolean mMenuIsCached;
/** The item a remote starts from: the active slot, Q or U (#91). */
private TextView mFocusItem;
private TextView mSettingsBtn;
private TextView mWebUiBtn;
TextView[] camButtons;
Expand Down Expand Up @@ -121,7 +123,8 @@ void showMenu(View anchor, boolean quadEnabled, int mActive, String version,
int margin = dp(12);
mMenuPopup.showAtLocation(anchor, Gravity.TOP | Gravity.START, margin, margin);
mMenuPopup.setOnDismissListener(() -> mMenuPopup = null);
focusForKeys(mMenuLayout);
mFocusItem = focusItem(quadEnabled, mActive);
focusForKeys(mMenuLayout, mFocusItem);
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
return;
}

Expand All @@ -134,6 +137,15 @@ public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) dismissPopup();
return true;
}
// While the layout itself has the focus (opened by touch, #65), a D-pad key
// finds no item: they all lie inside it, not beside it. The first key moves
// the focus to the active item instead (#91)
if (findFocus() == this && isNavigationKey(event.getKeyCode())
&& event.getAction() == KeyEvent.ACTION_DOWN) {
TextView item = mFocusItem;
// Consumed only if the focus really moved; otherwise the key goes on
if (item != null && item.requestFocus()) return true;
}
return super.dispatchKeyEvent(event);
}
};
Expand All @@ -146,7 +158,6 @@ public boolean dispatchKeyEvent(KeyEvent event) {
popup.showAtLocation(anchor, Gravity.TOP | Gravity.START, margin, margin);
mMenuPopup = popup;
popup.setOnDismissListener(() -> mMenuPopup = null);
focusForKeys(layout);

LinearLayout camRow = new LinearLayout(activity);
camRow.setOrientation(LinearLayout.HORIZONTAL);
Expand Down Expand Up @@ -256,6 +267,8 @@ public boolean dispatchKeyEvent(KeyEvent event) {

mMenuLayout = layout;
mMenuIsCached = true;
mFocusItem = focusItem(quadEnabled, mActive);
focusForKeys(layout, mFocusItem);
}

private void updateCachedMenu(boolean quadEnabled, int mActive, String[] mHosts) {
Expand Down Expand Up @@ -290,11 +303,37 @@ private void updateCachedMenu(boolean quadEnabled, int mActive, String[] mHosts)

/**
* A menu opened by touch has no focused view, so keys never reach the layout and Escape
* did not close it. In touch mode the layout itself takes focus; with a remote, focus
* stays on the menu items as before.
* did not close it. In touch mode the layout itself takes focus (#65); opened with a
* remote, the active item does (#91).
*/
private static void focusForKeys(View layout) {
if (layout.isInTouchMode()) layout.requestFocus();
private static void focusForKeys(View layout, View item) {
if (layout.isInTouchMode() || item == null) layout.requestFocus();
else item.requestFocus();
}

/** The highlighted item of the slot row: the active slot, Q in multi-view, U. */
private TextView focusItem(boolean quadEnabled, int mActive) {
if (udpShown && mCachedUdpBtn != null) return mCachedUdpBtn;
if (quadEnabled && mCachedQuadBtn != null) return mCachedQuadBtn;
if (camButtons != null && mActive >= 0 && mActive < camButtons.length) {
return camButtons[mActive];
}
return null;
}

static boolean isNavigationKey(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_TAB:
return true;
default:
return false;
}
}

private TextView createItem(String title) {
Expand All @@ -303,6 +342,8 @@ private TextView createItem(String title) {
text.setPadding(dp(8), dp(6), dp(8), dp(6));
text.setTextColor(Color.WHITE);
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
// Reachable with a remote (#91); not only by the clickable default of newer Androids
text.setFocusable(true);
focusChange(text);
return text;
}
Expand Down
Loading