diff --git a/androidtv/basetv/basetv.py b/androidtv/basetv/basetv.py index 50bfecb0..313ac65a 100644 --- a/androidtv/basetv/basetv.py +++ b/androidtv/basetv/basetv.py @@ -173,6 +173,14 @@ def _cmd_current_app(self): ): return constants.CMD_CURRENT_APP_ASKEY_STI6130 + # Is this an Askey ADT-3 Device (e.g. HTC MagicubOS projectors)? + if ( + self.DEVICE_ENUM == constants.DeviceEnum.ANDROIDTV + and "askey" in self.device_properties.get("manufacturer", "") + and "adt3" in self.device_properties.get("product_id", "") + ): + return constants.CMD_CURRENT_APP_ASKEY_ADT3 + # Is this an Android 11 device? if self.DEVICE_ENUM == constants.DeviceEnum.ANDROIDTV and self.device_properties.get("sw_version", "") == "11": return constants.CMD_CURRENT_APP11 @@ -210,6 +218,14 @@ def _cmd_current_app_media_session_state(self): ): return constants.CMD_CURRENT_APP_MEDIA_SESSION_STATE_ASKEY_STI6130 + # Is this an Askey ADT-3 Device (e.g. HTC MagicubOS projectors)? + if ( + self.DEVICE_ENUM == constants.DeviceEnum.ANDROIDTV + and "askey" in self.device_properties.get("manufacturer", "") + and "adt3" in self.device_properties.get("product_id", "") + ): + return constants.CMD_CURRENT_APP_MEDIA_SESSION_STATE_ASKEY_ADT3 + # Is this a Google Chromecast Android TV? if ( self.DEVICE_ENUM == constants.DeviceEnum.ANDROIDTV @@ -311,6 +327,14 @@ def _cmd_launch_app(self, app): if self.DEVICE_ENUM == constants.DeviceEnum.FIRETV: return constants.CMD_LAUNCH_APP_FIRETV.format(app) + # Is this an Askey ADT-3 Device (e.g. HTC MagicubOS projectors)? + if ( + self.DEVICE_ENUM == constants.DeviceEnum.ANDROIDTV + and "askey" in self.device_properties.get("manufacturer", "") + and "adt3" in self.device_properties.get("product_id", "") + ): + return constants.CMD_LAUNCH_APP_ASKEY_ADT3.format(app) + # Is this an Android 11 device? if self.DEVICE_ENUM == constants.DeviceEnum.ANDROIDTV and self.device_properties.get("sw_version", "") == "11": return constants.CMD_LAUNCH_APP11.format(app) diff --git a/androidtv/constants.py b/androidtv/constants.py index 6fa36bb3..1c68f372 100644 --- a/androidtv/constants.py +++ b/androidtv/constants.py @@ -132,6 +132,17 @@ class DeviceEnum(IntEnum): #: Output identifier for current/focused application (for a Askey STI6130) CMD_CURRENT_APP_ASKEY_STI6130 = CMD_DEFINE_CURRENT_APP_VARIABLE_ASKEY_STI6130 + " && echo $CURRENT_APP " +#: Assign focused application identifier to ``CURRENT_APP`` variable (for an Askey ADT-3, e.g. HTC MagicubOS projectors) +#: On this device (Android 14, sdk 34), ``dumpsys window windows`` no longer includes ``mCurrentFocus``/ +#: ``mFocusedApp``/``mObscuringWindow``/``imeLayeringTarget`` etc. Those fields are only present in the +#: unfiltered ``dumpsys window`` output. +CMD_DEFINE_CURRENT_APP_VARIABLE_ASKEY_ADT3 = ( + "CURRENT_APP=$(dumpsys window | grep -E 'mCurrentFocus|mFocusedApp|mObscuringWindow') && " + CMD_PARSE_CURRENT_APP11 +) + +#: Output identifier for current/focused application (for an Askey ADT-3) +CMD_CURRENT_APP_ASKEY_ADT3 = CMD_DEFINE_CURRENT_APP_VARIABLE_ASKEY_ADT3 + " && echo $CURRENT_APP" + #: Assign focused application identifier to ``CURRENT_APP`` variable (for a Google TV device) CMD_DEFINE_CURRENT_APP_VARIABLE_GOOGLE_TV = ( @@ -188,6 +199,11 @@ class DeviceEnum(IntEnum): CMD_DEFINE_CURRENT_APP_VARIABLE13.replace("{", "{{").replace("}", "}}") + " && " + CMD_LAUNCH_APP_CONDITION ) +#: Launch an app if it is not already the current app on an Askey ADT-3 device +CMD_LAUNCH_APP_ASKEY_ADT3 = ( + CMD_DEFINE_CURRENT_APP_VARIABLE_ASKEY_ADT3.replace("{", "{{").replace("}", "}}") + " && " + CMD_LAUNCH_APP_CONDITION +) + #: Launch an app on a Fire TV device CMD_LAUNCH_APP_FIRETV = ( CMD_DEFINE_CURRENT_APP_VARIABLE.replace("{", "{{").replace("}", "}}") + " && " + CMD_LAUNCH_APP_CONDITION_FIRETV @@ -216,6 +232,9 @@ class DeviceEnum(IntEnum): #: Determine the current app and get the state from ``dumpsys media_session`` for a Askey STI6130 CMD_CURRENT_APP_MEDIA_SESSION_STATE_ASKEY_STI6130 = CMD_CURRENT_APP_ASKEY_STI6130 + " && " + CMD_MEDIA_SESSION_STATE +#: Determine the current app and get the state from ``dumpsys media_session`` for an Askey ADT-3 +CMD_CURRENT_APP_MEDIA_SESSION_STATE_ASKEY_ADT3 = CMD_CURRENT_APP_ASKEY_ADT3 + " && " + CMD_MEDIA_SESSION_STATE + #: Determine the current app and get the state from ``dumpsys media_session`` for a Google TV device CMD_CURRENT_APP_MEDIA_SESSION_STATE_GOOGLE_TV = CMD_CURRENT_APP_GOOGLE_TV + " && " + CMD_MEDIA_SESSION_STATE @@ -226,7 +245,12 @@ class DeviceEnum(IntEnum): CMD_INSTALLED_APPS = "pm list packages" #: Determine if the device is on -CMD_SCREEN_ON = "(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || dumpsys display | grep -q 'mScreenState=ON')" +#: The ``dumpsys display`` fallback buffers output via command substitution before grepping it, rather than +#: piping directly into ``grep -q``. On devices with large ``dumpsys display`` output, ``grep -q`` closes its +#: end of the pipe as soon as it finds a match, and the still-writing ``dumpsys`` process can then fail with +#: "Failed to write while dumping service display: Broken pipe" -- text which gets prepended to the result +#: and corrupts callers (like ``screen_on_awake_wake_lock_size``) that parse it as a literal "1"/"0" string. +CMD_SCREEN_ON = "(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || echo \"$(dumpsys display)\" | grep -q 'mScreenState=ON')" #: Get the "STREAM_MUSIC" block from ``dumpsys audio`` CMD_STREAM_MUSIC = r"dumpsys audio | grep '\- STREAM_MUSIC:' -A 11" diff --git a/tests/generate_test_constants.py b/tests/generate_test_constants.py index 7bc203e1..b3bee326 100644 --- a/tests/generate_test_constants.py +++ b/tests/generate_test_constants.py @@ -19,6 +19,7 @@ "CMD_DEFINE_CURRENT_APP_VARIABLE12", "CMD_DEFINE_CURRENT_APP_VARIABLE13", "CMD_DEFINE_CURRENT_APP_VARIABLE_ASKEY_STI6130", + "CMD_DEFINE_CURRENT_APP_VARIABLE_ASKEY_ADT3", "CMD_DEFINE_CURRENT_APP_VARIABLE_GOOGLE_TV", "CMD_LAUNCH_APP_CONDITION", "CMD_LAUNCH_APP_CONDITION_FIRETV", diff --git a/tests/test_constants.py b/tests/test_constants.py index d2f9ca5f..970733b1 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -100,6 +100,12 @@ def test_constants(self): constants.CMD_CURRENT_APP13, r"CURRENT_APP=$(dumpsys window windows | grep -E -m 1 'imeLayeringTarget|imeInputTarget|imeControlTarget') && CURRENT_APP=${CURRENT_APP%%/*} && CURRENT_APP=${CURRENT_APP##* } && echo $CURRENT_APP", ) + # CMD_CURRENT_APP_ASKEY_ADT3 + self.assertCommand( + constants.CMD_CURRENT_APP_ASKEY_ADT3, + r"CURRENT_APP=$(dumpsys window | grep -E 'mCurrentFocus|mFocusedApp|mObscuringWindow') && CURRENT_APP=${CURRENT_APP%%/*} && CURRENT_APP=${CURRENT_APP##* } && echo $CURRENT_APP", + ) + # CMD_CURRENT_APP_ASKEY_STI6130 self.assertCommand( constants.CMD_CURRENT_APP_ASKEY_STI6130, @@ -135,6 +141,12 @@ def test_constants(self): constants.CMD_CURRENT_APP_MEDIA_SESSION_STATE13, r"CURRENT_APP=$(dumpsys window windows | grep -E -m 1 'imeLayeringTarget|imeInputTarget|imeControlTarget') && CURRENT_APP=${CURRENT_APP%%/*} && CURRENT_APP=${CURRENT_APP##* } && echo $CURRENT_APP && dumpsys media_session | grep -A 100 'Sessions Stack' | grep -A 100 $CURRENT_APP | grep -m 1 'state=PlaybackState {'", ) + # CMD_CURRENT_APP_MEDIA_SESSION_STATE_ASKEY_ADT3 + self.assertCommand( + constants.CMD_CURRENT_APP_MEDIA_SESSION_STATE_ASKEY_ADT3, + r"CURRENT_APP=$(dumpsys window | grep -E 'mCurrentFocus|mFocusedApp|mObscuringWindow') && CURRENT_APP=${CURRENT_APP%%/*} && CURRENT_APP=${CURRENT_APP##* } && echo $CURRENT_APP && dumpsys media_session | grep -A 100 'Sessions Stack' | grep -A 100 $CURRENT_APP | grep -m 1 'state=PlaybackState {'", + ) + # CMD_CURRENT_APP_MEDIA_SESSION_STATE_ASKEY_STI6130 self.assertCommand( constants.CMD_CURRENT_APP_MEDIA_SESSION_STATE_ASKEY_STI6130, @@ -192,6 +204,12 @@ def test_constants(self): r"CURRENT_APP=$(dumpsys window windows | grep -E -m 1 'imeLayeringTarget|imeInputTarget|imeControlTarget') && CURRENT_APP=${{CURRENT_APP%%/*}} && CURRENT_APP=${{CURRENT_APP##* }} && if [ $CURRENT_APP != '{0}' ]; then monkey -p {0} -c android.intent.category.LEANBACK_LAUNCHER --pct-syskeys 0 1; fi", ) + # CMD_LAUNCH_APP_ASKEY_ADT3 + self.assertCommand( + constants.CMD_LAUNCH_APP_ASKEY_ADT3, + r"CURRENT_APP=$(dumpsys window | grep -E 'mCurrentFocus|mFocusedApp|mObscuringWindow') && CURRENT_APP=${{CURRENT_APP%%/*}} && CURRENT_APP=${{CURRENT_APP##* }} && if [ $CURRENT_APP != '{0}' ]; then monkey -p {0} -c android.intent.category.LEANBACK_LAUNCHER --pct-syskeys 0 1; fi", + ) + # CMD_LAUNCH_APP_FIRETV self.assertCommand( constants.CMD_LAUNCH_APP_FIRETV, @@ -231,13 +249,13 @@ def test_constants(self): # CMD_SCREEN_ON self.assertCommand( constants.CMD_SCREEN_ON, - r"(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || dumpsys display | grep -q 'mScreenState=ON')", + r"""(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || echo "$(dumpsys display)" | grep -q 'mScreenState=ON')""", ) # CMD_SCREEN_ON_AWAKE_WAKE_LOCK_SIZE self.assertCommand( constants.CMD_SCREEN_ON_AWAKE_WAKE_LOCK_SIZE, - r"(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || dumpsys display | grep -q 'mScreenState=ON') && echo -e '1\c' || echo -e '0\c' && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\c' || echo -e '0\c' && dumpsys power | grep Locks | grep 'size='", + r"""(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || echo "$(dumpsys display)" | grep -q 'mScreenState=ON') && echo -e '1\c' || echo -e '0\c' && dumpsys power | grep mWakefulness | grep -q Awake && echo -e '1\c' || echo -e '0\c' && dumpsys power | grep Locks | grep 'size='""", ) # CMD_SERIALNO @@ -249,25 +267,25 @@ def test_constants(self): # CMD_TURN_OFF_ANDROIDTV self.assertCommand( constants.CMD_TURN_OFF_ANDROIDTV, - r"(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || dumpsys display | grep -q 'mScreenState=ON') && input keyevent 26", + r"""(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || echo "$(dumpsys display)" | grep -q 'mScreenState=ON') && input keyevent 26""", ) # CMD_TURN_OFF_FIRETV self.assertCommand( constants.CMD_TURN_OFF_FIRETV, - r"(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || dumpsys display | grep -q 'mScreenState=ON') && input keyevent 223", + r"""(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || echo "$(dumpsys display)" | grep -q 'mScreenState=ON') && input keyevent 223""", ) # CMD_TURN_ON_ANDROIDTV self.assertCommand( constants.CMD_TURN_ON_ANDROIDTV, - r"(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || dumpsys display | grep -q 'mScreenState=ON') || input keyevent 26", + r"""(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || echo "$(dumpsys display)" | grep -q 'mScreenState=ON') || input keyevent 26""", ) # CMD_TURN_ON_FIRETV self.assertCommand( constants.CMD_TURN_ON_FIRETV, - r"(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || dumpsys display | grep -q 'mScreenState=ON') || (input keyevent 26 && input keyevent 3)", + r"""(dumpsys power | grep 'Display Power' | grep -q 'state=ON' || dumpsys power | grep -q 'mScreenOn=true' || echo "$(dumpsys display)" | grep -q 'mScreenState=ON') || (input keyevent 26 && input keyevent 3)""", ) # CMD_VERSION