te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>android tv - DPAD events handling with Jetpack Compose when Talkback is ON (Media 3 exoplayer) - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android tv - DPAD events handling with Jetpack Compose when Talkback is ON (Media 3 exoplayer) - Stack Overflow

programmeradmin5浏览0评论

I am using ExoPlayer (Media3) in Android TV with compose, a streaming application, but when Accessibility/Talkback is ON, DPAD events are not being captured at client side, only back/Enter/Center button is getting captured.

In the normal mode i.e. Accessibility/Talkback is OFF all is working as expected.

But some how in Fire TV am getting all dpad event when Accessibility is ON and OFF

Below is the code is to inflate the media3 exoplayer.

@Composable
private fun CustomPlayerView(
    modifier: Modifier = Modifier,
    areControlsVisible: Boolean,
    isSubtitlesVisible: Boolean,
    isInTv: Boolean = false,
    isInPipMode: Boolean = false,
    resizeMode: Int = AspectRatioFrameLayout.RESIZE_MODE_FIT,
    getPlayerView: (view: View) -> Unit
) {
    val playerManager by rememberLocalPlayer()

    AndroidViewBinding(
        modifier = modifier,
        factory = { inflater, viewGroup, attachToParent ->
            val binding = CustomPlayerBinding.inflate(inflater, viewGroup, attachToParent)

            binding.apply {
                root.isClickable = false
                root.isFocusable = false

                playerView.run {
                    [email protected] = resizeMode
                    [email protected] = playerManager.player

                    // Show the controls forever
                    controllerShowTimeoutMs = Int.MAX_VALUE
                    showController()
                 }
            }

            return@AndroidViewBinding binding
        }

Player screen code:

            CustomPlayerView(
                        areControlsVisible = controlsUIState.controlUIState is ControlUISate.Show,
                        isInPipMode = false,
                        playbackState = playbackState,
                        onPlayerAction = onPlayerControlsAction,
                        onInitialize = {
                            // Initialise player
                        },
                        onRelease = { isForceReleasing ->
                           // Handle release
                        },
                        modifier = Modifier
                            .focusOnInitialVisibility()
                            .focusable()
                            .handleDPadKeyEvents(
                                isSeeking = trickPlayUIState.isSeeking,
                                onEnter = { _, _ ->
                                    handlePlayBack()
                                },
                                onUp = { _, _ ->
                                    //Handle Dpad UP
                                },
                                onLeft = { isLongPress, isReleased ->
                                    //Handle Dpad LEFT
                                },
                                onRight = { isLongPress, isReleased ->
                                    //Handle Dpad RIGHT
                                },
                                onMediaKeyFf = { isLongPress, isReleased ->
                                    //Handle Dpad FF
                                },
                                onMediaKeyRw = { isLongPress, isReleased ->
                                    //Handle Dpad RW
                                },
                                onKeyDown = { _, _ ->
                                    //Handle Dpad DOWN
                                },
                                onMediaKey = {
                                    //Handle Dpad Media Key generic
                                },
                                onMediaKeyPlayPause = { _, _ ->
                                    handlePlayBack()
                                },
                                shouldConsumeRightEvent = true,
                                shouldConsumeLefEvent = true,
                                shouldConsumeEventPlayPauseEvent = true
                            ),
                    )

Dpad ext. fun

      /**
       * Handles horizontal (Left & Right) D-Pad Keys and consumes the event(s) so 
        that the  focus doesn't
       * accidentally move to another element.
      * */

     @Composable
     fun Modifier.handleDPadKeyEvents(
      onLeft: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onRight: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onUp: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onDown: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onEnter: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onKeyDown: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onBack: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onMediaKey: ((keyCode: Int) -> Unit)? = null, // Callback for media keys
      onMediaKeyFf: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onMediaKeyRw: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      onMediaKeyPlayPause: ((isLongPress: Boolean, isReleased: Boolean) -> Unit)? = null,
      shouldConsumeEvent: Boolean = false,
      shouldConsumeLefEvent: Boolean = false,
      shouldConsumeRightEvent: Boolean = false,
      shouldConsumeEventPlayPauseEvent: Boolean = false,
      isSeeking: Boolean = false
    ): Modifier {
    val holdThreshold = 5 // Milliseconds for press-and-hold detection
    var pressStartTime by remember { mutableIntStateOf(0) }
    var isLongPressDetected by remember { mutableStateOf(false) } // Flag to track if long press has been detected

LaunchedEffect(isSeeking) {
    // This is required to reset the variables when long press and move focus to another view.
    // This case On Action up of previous focussed view is not called, so that blocking us to reset these variable
    if (!isSeeking) {
        pressStartTime = 0
        isLongPressDetected = false
    }
}

return this.onPreviewKeyEvent { event ->
    val nativeKeyEvent = event.nativeKeyEvent

    fun handleKeyAction(block: (isLongPress: Boolean, isReleased: Boolean) -> Unit) {
        when (nativeKeyEvent.action) {
            KeyEvent.ACTION_UP -> {

                block(isLongPressDetected, true)
                // Reset variables
                pressStartTime = 0
                isLongPressDetected = false
            }

            KeyEvent.ACTION_DOWN -> {
                // only for the first press, as we have requirement, like on key down only we need to show controller.
                if (pressStartTime == 0) onKeyDown?.invoke(false, false)
                pressStartTime += 1
                if (pressStartTime > holdThreshold) {
                    block(true, false)
                    isLongPressDetected = true // Mark that long press was detected
                }
            }
        }
    }

    when {
        // Handle D-Pad Navigation Keys
        DPadEventsKeyCodes.contains(nativeKeyEvent.keyCode) -> {
            when (nativeKeyEvent.keyCode) {
                KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT -> {
                    onLeft?.let {
                        handleKeyAction(it::invoke)
                        return@onPreviewKeyEvent shouldConsumeLefEvent
                    }
                }

                KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT -> {
                    onRight?.let {
                        handleKeyAction(it::invoke)
                        return@onPreviewKeyEvent shouldConsumeRightEvent
                    }
                }

                KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP -> {
                    onUp?.let { handleKeyAction(it::invoke) }
                }

                KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN -> {
                    onDown?.let { handleKeyAction(it::invoke) }
                }

                KeyEvent.KEYCODE_DPAD_CENTER, KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_NUMPAD_ENTER -> {
                    onEnter?.let { handleKeyAction(it::invoke) }
                }
            }
            shouldConsumeEvent
        }

        // Handle Media Key Events, FF/RW for media keys, rest will be handled by system .
        MediaKeyCodes.contains(nativeKeyEvent.keyCode) -> {
            when (nativeKeyEvent.keyCode) {
                KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> {
                    onMediaKeyFf?.let {
                        handleKeyAction(it::invoke)
                        return@onPreviewKeyEvent true
                    }
                }

                KeyEvent.KEYCODE_MEDIA_REWIND -> {
                    onMediaKeyRw?.let {
                        handleKeyAction(it::invoke)
                        return@onPreviewKeyEvent true
                    }
                }
                // Handle  Dedicated Media button Play/Pause at client side
                KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_MEDIA_PAUSE -> {
                    onMediaKeyPlayPause?.let {
                        handleKeyAction(it::invoke)
                        return@onPreviewKeyEvent shouldConsumeEventPlayPauseEvent
                    }
                }

                else -> {
                    onMediaKey?.invoke(nativeKeyEvent.keyCode) // Invoke media key callback
                }
            }
            shouldConsumeEvent
        }

        // Handle System Key Events, we may need to handle like handleKeyAction(onMediaKey?.invoke) for system keys.
        SystemKeyCodes.contains(nativeKeyEvent.keyCode) && nativeKeyEvent.action == KeyEvent.ACTION_DOWN -> {
            when (nativeKeyEvent.keyCode) {
                KeyEvent.KEYCODE_BACK -> onBack?.invoke(false, false)
                else -> onKeyDown?.invoke(false, false)
            }
            shouldConsumeEvent
        }

        else -> shouldConsumeEvent
    }
}

}

Initial focus ext. fun

 /**
  * This modifier can be used to gain focus on a focusable component when it becomes 
    visible
  * for the first time.
  * */
    @Composable
    fun Modifier.focusOnInitialVisibility(
    isVisible: MutableState<Boolean> = remember { mutableStateOf(false) }
   ): Modifier {
    val focusRequester = remember { FocusRequester() }

       return focusRequester(focusRequester)
       .onGloballyPositioned {
          if (!isVisible.value) {
            focusRequester.requestFocus()
            isVisible.value = true
          }
        }
  }

Does anyone have any idea how can I properly handle those events? The controller doesn't show up on top of the video due to this and can't perform any events such as play/pause/seek, etc.

Any help will be greatly appreciated.

Thank you!.

发布评论

评论列表(0)

  1. 暂无评论