package com.evsuite.abrp; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * Reads one steering-wheel key report from the broadcast's extras. * * Seen on the MG4 (2026-10-17, on-screen key log): the head unit sends * android.intent.extra.hardkey.keycode = 287 (Integer) * android.intent.extra.hardkey.down = true (Boolean, then false on release) * android.intent.extra.hardkey.longpress = false (Boolean) * Names are matched on their last dot-separated part, case-insensitively, so this exact form * and simpler ones ("keyCode", "down", …) are both understood. * * Android-free (the extras arrive as a plain map), unit-tested. */ final class SteeringKeyReport { final Integer code; final Boolean down; final boolean longPress; SteeringKeyReport(Integer code, Boolean down, boolean longPress) { this.code = code; this.down = down; this.longPress = longPress; } private static final String[] CODE_NAMES = {"keycode", "key_code", "keyvalue", "key_value", "code", "key"}; private static final String[] ACTION_NAMES = {"keyaction", "key_action", "action", "keystate", "key_state", "state", "event", "type"}; private static final String[] DOWN_NAMES = {"down", "isdown", "pressed", "ispressed"}; private static final String[] LONG_NAMES = {"longpress", "islongpress", "long_press", "islong", "long"}; static SteeringKeyReport parse(Map extras) { Map byName = new HashMap<>(); if (extras != null) { for (Map.Entry e : extras.entrySet()) { String key = e.getKey(); String last = key.substring(key.lastIndexOf('.') + 1).toLowerCase(Locale.US); byName.putIfAbsent(last, e.getValue()); } } Integer code = firstInt(byName, CODE_NAMES); Boolean down = null; boolean longPress = false; // KeyEvent's own convention for a numeric action: 0 down, 1 up; anything else is long. Integer action = firstInt(byName, ACTION_NAMES); if (action != null) { if (action == 0) down = true; else if (action == 1) down = false; else longPress = true; } Boolean explicitDown = firstBoolean(byName, DOWN_NAMES); if (explicitDown != null) down = explicitDown; Boolean explicitLong = firstBoolean(byName, LONG_NAMES); if (explicitLong != null) longPress = explicitLong; return new SteeringKeyReport(code, down, longPress); } private static Integer firstInt(Map byName, String[] names) { for (String n : names) { Object v = byName.get(n); if (v instanceof Integer) return (Integer) v; if (v instanceof Long) return (int) (long) (Long) v; if (v instanceof String) { try { return Integer.parseInt(((String) v).trim()); } catch (NumberFormatException ignored) { } } } return null; } private static Boolean firstBoolean(Map byName, String[] names) { for (String n : names) { Object v = byName.get(n); if (v instanceof Boolean) return (Boolean) v; } return null; } }