diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a4927daf..c6b8d4bb 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -120,7 +120,7 @@ jobs: ./gradlew --build-cache dpadrecyclerview-compose:connectedDebugAndroidTest ./gradlew --build-cache sample:connectedDebugAndroidTest ./gradlew --build-cache dpadrecyclerview-testing:connectedDebugAndroidTest - ./gradlew --build-cache dpadrecyclerview:connectedDebugAndroidTest --info + ./gradlew --build-cache dpadrecyclerview:connectedDebugAndroidTest - name: Upload artifacts uses: actions/upload-artifact@v3 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index d05eeddf..414469cb 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -45,7 +45,7 @@ jobs: matrix: arch: [ x86 ] target: [ android-tv ] - api-level: [27] + api-level: [22, 25, 27, 28, 29, 30, 31] profile: [tv_1080p] steps: - name: checkout diff --git a/dpadrecyclerview/src/androidTest/kotlin/com/rubensousa/dpadrecyclerview/test/tests/focus/NestedFocusListenerTest.kt b/dpadrecyclerview/src/androidTest/kotlin/com/rubensousa/dpadrecyclerview/test/tests/focus/NestedFocusListenerTest.kt index e7d0ca49..bbfdefef 100644 --- a/dpadrecyclerview/src/androidTest/kotlin/com/rubensousa/dpadrecyclerview/test/tests/focus/NestedFocusListenerTest.kt +++ b/dpadrecyclerview/src/androidTest/kotlin/com/rubensousa/dpadrecyclerview/test/tests/focus/NestedFocusListenerTest.kt @@ -19,6 +19,9 @@ package com.rubensousa.dpadrecyclerview.test.tests.focus import androidx.core.view.get import androidx.fragment.app.testing.FragmentScenario import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withTagValue import com.google.common.truth.Truth.assertThat import com.rubensousa.dpadrecyclerview.DpadRecyclerView import com.rubensousa.dpadrecyclerview.test.TestNestedListFragment @@ -29,7 +32,10 @@ import com.rubensousa.dpadrecyclerview.testfixtures.DpadFocusEvent import com.rubensousa.dpadrecyclerview.testfixtures.recording.ScreenRecorderRule import com.rubensousa.dpadrecyclerview.testing.KeyEvents import com.rubensousa.dpadrecyclerview.testing.R +import com.rubensousa.dpadrecyclerview.testing.actions.DpadRecyclerViewActions import com.rubensousa.dpadrecyclerview.testing.rules.DisableIdleTimeoutRule +import org.hamcrest.Matchers +import org.hamcrest.core.AllOf.allOf import org.junit.Before import org.junit.Rule import org.junit.Test @@ -127,6 +133,27 @@ class NestedFocusListenerTest { } } + @Test + fun testParentRecyclerViewStillReceivesFocusIfChildHasNoListener() { + // given + val focusEvents = 6 + Espresso.onView( + allOf( + withId(com.rubensousa.dpadrecyclerview.test.R.id.nestedRecyclerView), + withTagValue(Matchers.`is`(0)) + ) + ).perform(DpadRecyclerViewActions.execute("Clear listener") { recyclerView -> + recyclerView.clearOnViewFocusedListeners() + }) + + // when + KeyEvents.pressRight(times = focusEvents - 1) + waitForIdleScrollState() + + // then + assertThat(getParentFocusEvents()).hasSize(focusEvents) + } + private fun getChildFocusEvents(): List { var events = listOf() fragmentScenario.onFragment { fragment -> diff --git a/dpadrecyclerview/src/main/java/com/rubensousa/dpadrecyclerview/layoutmanager/PivotLayoutManager.kt b/dpadrecyclerview/src/main/java/com/rubensousa/dpadrecyclerview/layoutmanager/PivotLayoutManager.kt index b6f5a237..f6353f62 100644 --- a/dpadrecyclerview/src/main/java/com/rubensousa/dpadrecyclerview/layoutmanager/PivotLayoutManager.kt +++ b/dpadrecyclerview/src/main/java/com/rubensousa/dpadrecyclerview/layoutmanager/PivotLayoutManager.kt @@ -139,7 +139,6 @@ class PivotLayoutManager(properties: Properties) : RecyclerView.LayoutManager(), // If we have focus, save it temporarily since the views will change and we might lose it hadFocusBeforeLayout = hasFocus() scroller.cancelSmoothScroller() - pivotSelector.onLayoutChildren(state) pivotLayout.onLayoutChildren(recycler, state) layoutCompletedListener?.onLayoutCompleted(state) } diff --git a/dpadrecyclerview/src/main/java/com/rubensousa/dpadrecyclerview/layoutmanager/PivotSelector.kt b/dpadrecyclerview/src/main/java/com/rubensousa/dpadrecyclerview/layoutmanager/PivotSelector.kt index a5badcaa..c7926f77 100644 --- a/dpadrecyclerview/src/main/java/com/rubensousa/dpadrecyclerview/layoutmanager/PivotSelector.kt +++ b/dpadrecyclerview/src/main/java/com/rubensousa/dpadrecyclerview/layoutmanager/PivotSelector.kt @@ -106,8 +106,8 @@ internal class PivotSelector( fun focus(view: View) { view.requestFocus() - // Exit early if there's no one listening for focus events - if (focusListeners.isEmpty() || isRetainingFocus) { + // Do not notify listeners if we are retaining focus + if (isRetainingFocus) { return } val currentRecyclerView = recyclerView ?: return @@ -213,10 +213,6 @@ internal class PivotSelector( return max(0, min(itemCount - 1, position)) } - fun onLayoutChildren(state: RecyclerView.State) { - - } - fun onLayoutCompleted() { if (isSelectionUpdatePending) { isSelectionUpdatePending = false