Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 037392f3 authored by Bryce Lee's avatar Bryce Lee Committed by Android (Google) Code Review
Browse files

Merge "Sync scroll position between glanceable hub and edit mode." into main

parents cda8af31 b3f8ff4c
Loading
Loading
Loading
Loading
+22 −1
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -171,7 +172,11 @@ fun CommunalHub(
    var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
    var toolbarSize: IntSize? by remember { mutableStateOf(null) }
    var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
    val gridState = rememberLazyGridState()

    val gridState =
        rememberLazyGridState(viewModel.savedFirstScrollIndex, viewModel.savedFirstScrollOffset)
    viewModel.clearPersistedScrollPosition()

    val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel)
    val reorderingWidgets by viewModel.reorderingWidgets.collectAsStateWithLifecycle()
    val selectedKey = viewModel.selectedKey.collectAsStateWithLifecycle()
@@ -187,6 +192,8 @@ fun CommunalHub(
    val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
    val contentOffset = beforeContentPadding(contentPadding).toOffset()

    ObserveScrollEffect(gridState, viewModel)

    if (!viewModel.isEditMode) {
        ScrollOnUpdatedLiveContentEffect(communalContent, gridState)
    }
@@ -420,6 +427,20 @@ private fun DisclaimerBottomSheetContent(onButtonClicked: () -> Unit) {
    }
}

@Composable
private fun ObserveScrollEffect(
    gridState: LazyGridState,
    communalViewModel: BaseCommunalViewModel
) {

    LaunchedEffect(gridState) {
        snapshotFlow {
                Pair(gridState.firstVisibleItemIndex, gridState.firstVisibleItemScrollOffset)
            }
            .collect { communalViewModel.onScrollPositionUpdated(it.first, it.second) }
    }
}

/**
 * Observes communal content and scrolls to any added or updated live content, e.g. a new media
 * session is started, or a paused timer is resumed.
+16 −1
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.fakeCommunalSmartspaceRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
@@ -76,6 +77,8 @@ import org.mockito.Mockito
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.eq
import org.mockito.kotlin.spy

@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -94,6 +97,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
    private lateinit var smartspaceRepository: FakeCommunalSmartspaceRepository
    private lateinit var mediaRepository: FakeCommunalMediaRepository
    private lateinit var communalSceneInteractor: CommunalSceneInteractor
    private lateinit var communalInteractor: CommunalInteractor

    private val testableResources = context.orCreateTestableResources

@@ -108,6 +112,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
        smartspaceRepository = kosmos.fakeCommunalSmartspaceRepository
        mediaRepository = kosmos.fakeCommunalMediaRepository
        communalSceneInteractor = kosmos.communalSceneInteractor
        communalInteractor = spy(kosmos.communalInteractor)
        kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
        kosmos.fakeUserTracker.set(
            userInfos = listOf(MAIN_USER_INFO),
@@ -119,7 +124,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
        underTest =
            CommunalEditModeViewModel(
                communalSceneInteractor,
                kosmos.communalInteractor,
                communalInteractor,
                kosmos.communalSettingsInteractor,
                kosmos.keyguardTransitionInteractor,
                mediaHost,
@@ -346,6 +351,16 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
            assertThat(showDisclaimer).isFalse()
        }

    @Test
    fun scrollPosition_persistedOnEditCleanup() {
        val index = 2
        val offset = 30
        underTest.onScrollPositionUpdated(index, offset)
        underTest.cleanupEditModeState()

        verify(communalInteractor).setScrollPosition(eq(index), eq(offset))
    }

    private companion object {
        val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
        const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name"
+17 −1
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.fakeCommunalSmartspaceRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
@@ -97,7 +98,9 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -121,6 +124,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
    private lateinit var shadeTestUtil: ShadeTestUtil
    private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
    private lateinit var communalRepository: FakeCommunalSceneRepository
    private lateinit var communalInteractor: CommunalInteractor

    private lateinit var underTest: CommunalViewModel

@@ -154,6 +158,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {

        kosmos.powerInteractor.setAwakeForTest()

        communalInteractor = spy(kosmos.communalInteractor)

        underTest =
            CommunalViewModel(
                kosmos.testDispatcher,
@@ -164,7 +170,7 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
                kosmos.keyguardInteractor,
                mock<KeyguardIndicationController>(),
                kosmos.communalSceneInteractor,
                kosmos.communalInteractor,
                communalInteractor,
                kosmos.communalSettingsInteractor,
                kosmos.communalTutorialInteractor,
                kosmos.shadeInteractor,
@@ -779,6 +785,16 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
                .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
        }

    @Test
    fun scrollPosition_persistedOnEditEntry() {
        val index = 2
        val offset = 30
        underTest.onScrollPositionUpdated(index, offset)
        underTest.onOpenWidgetEditor(false)

        verify(communalInteractor).setScrollPosition(eq(index), eq(offset))
    }

    private suspend fun setIsMainUser(isMainUser: Boolean) {
        val user = if (isMainUser) MAIN_USER_INFO else SECONDARY_USER_INFO
        with(userRepository) {
+25 −0
Original line number Diff line number Diff line
@@ -541,4 +541,29 @@ constructor(
            )
        }
    }

    /**
     * {@link #setScrollPosition} persists the current communal grid scroll position (to volatile
     * memory) so that the next presentation of the grid (either as glanceable hub or edit mode) can
     * restore position.
     */
    fun setScrollPosition(firstVisibleItemIndex: Int, firstVisibleItemOffset: Int) {
        _firstVisibleItemIndex = firstVisibleItemIndex
        _firstVisibleItemOffset = firstVisibleItemOffset
    }

    fun resetScrollPosition() {
        _firstVisibleItemIndex = 0
        _firstVisibleItemOffset = 0
    }

    val firstVisibleItemIndex: Int
        get() = _firstVisibleItemIndex

    private var _firstVisibleItemIndex: Int = 0

    val firstVisibleItemOffset: Int
        get() = _firstVisibleItemOffset

    private var _firstVisibleItemOffset: Int = 0
}
+34 −0
Original line number Diff line number Diff line
@@ -59,6 +59,18 @@ abstract class BaseCommunalViewModel(
    /** Accessibility delegate to be set on CommunalAppWidgetHostView. */
    open val widgetAccessibilityDelegate: View.AccessibilityDelegate? = null

    /**
     * The up-to-date value of the grid scroll offset. persisted to interactor on
     * {@link #persistScrollPosition}
     */
    private var currentScrollOffset = 0

    /**
     * The up-to-date value of the grid scroll index. persisted to interactor on
     * {@link #persistScrollPosition}
     */
    private var currentScrollIndex = 0

    fun signalUserInteraction() {
        communalInteractor.signalUserInteraction()
    }
@@ -147,6 +159,28 @@ abstract class BaseCommunalViewModel(
    /** Called as the user request to show the customize widget button. */
    open fun onLongClick() {}

    /** Called when the grid scroll position has been updated. */
    open fun onScrollPositionUpdated(firstVisibleItemIndex: Int, firstVisibleItemScroll: Int) {
        currentScrollIndex = firstVisibleItemIndex
        currentScrollOffset = firstVisibleItemScroll
    }

    /** Stores scroll values to interactor. */
    protected fun persistScrollPosition() {
        communalInteractor.setScrollPosition(currentScrollIndex, currentScrollOffset)
    }

    /** Invoked after scroll values are used to initialize grid position. */
    open fun clearPersistedScrollPosition() {
        communalInteractor.setScrollPosition(0, 0)
    }

    val savedFirstScrollIndex: Int
        get() = communalInteractor.firstVisibleItemIndex

    val savedFirstScrollOffset: Int
        get() = communalInteractor.firstVisibleItemOffset

    /** Set the key of the currently selected item */
    fun setSelectedKey(key: String?) {
        _selectedKey.value = key
Loading