diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/common/pref/Prefs.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/common/pref/Prefs.kt index 6cf256de..fc8a330f 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/common/pref/Prefs.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/common/pref/Prefs.kt @@ -1,11 +1,8 @@ package io.github.droidkaigi.confsched2018.presentation.common.pref import com.chibatching.kotpref.KotprefModel -import com.chibatching.kotpref.enumpref.enumValuePref import io.github.droidkaigi.confsched2018.R -import io.github.droidkaigi.confsched2018.presentation.sessions.SessionTabMode import io.github.droidkaigi.confsched2018.util.ext.bool -import io.github.droidkaigi.confsched2018.util.ext.integer object Prefs : KotprefModel() { public override val kotprefName: String = "droidkaigi_prefs" @@ -25,14 +22,4 @@ object Prefs : KotprefModel() { default = context.bool(R.bool.pref_default_value_enable_reopen_previous_room_sessions), key = R.string.pref_key_enable_reopen_previous_room_sessions ) - var previousSessionTabMode by enumValuePref(SessionTabMode.ROOM) - var previousSessionTabId: Int by intPref( - default = context.integer(R.integer.pref_default_value_previous_session_tab_id) - ) - - fun initPreviousSessionPrefs() { - Prefs.previousSessionTabMode = SessionTabMode.ROOM - Prefs.previousSessionTabId = Prefs.context.integer( - integerRes = R.integer.pref_default_value_previous_session_tab_id) - } } diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/common/pref/PreviousSessionPrefs.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/common/pref/PreviousSessionPrefs.kt new file mode 100644 index 00000000..fca8a9a1 --- /dev/null +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/common/pref/PreviousSessionPrefs.kt @@ -0,0 +1,43 @@ +package io.github.droidkaigi.confsched2018.presentation.common.pref + +import com.chibatching.kotpref.KotprefModel +import com.chibatching.kotpref.enumpref.enumValuePref +import io.github.droidkaigi.confsched2018.R +import io.github.droidkaigi.confsched2018.presentation.sessions.SessionTabMode +import io.github.droidkaigi.confsched2018.util.ext.ScrollState +import io.github.droidkaigi.confsched2018.util.ext.integer + +object PreviousSessionPrefs : KotprefModel() { + public override val kotprefName: String = "previous_session_prefs" + var previousSessionTabMode by enumValuePref( + default = SessionTabMode.ROOM + ) + var previousSessionTabId: Int by intPref( + default = context.integer(R.integer.pref_default_value_previous_session_tab_id) + ) + private var previousSessionScrollPosition: Int by intPref( + default = context.integer(R.integer.pref_default_value_previous_session_scroll_position) + ) + private var previousSessionScrollOffset: Int by intPref( + default = context.integer(R.integer.pref_default_value_previous_session_scroll_offset) + ) + + fun initPreviousSessionPrefs() { + PreviousSessionPrefs.previousSessionTabMode = SessionTabMode.ROOM + PreviousSessionPrefs.previousSessionTabId = context.integer( + integerRes = R.integer.pref_default_value_previous_session_tab_id) + PreviousSessionPrefs.previousSessionScrollPosition = context.integer( + integerRes = R.integer.pref_default_value_previous_session_scroll_position) + PreviousSessionPrefs.previousSessionScrollOffset = context.integer( + integerRes = R.integer.pref_default_value_previous_session_scroll_offset) + } + + var scrollState: ScrollState + get() = ScrollState( + anchorPosition = previousSessionScrollPosition, + anchorOffset = previousSessionScrollOffset) + set(scrollState) { + previousSessionScrollPosition = scrollState.anchorPosition + previousSessionScrollOffset = scrollState.anchorOffset + } +} diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsFragment.kt index 1d762700..0e37106e 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsFragment.kt @@ -20,15 +20,19 @@ import io.github.droidkaigi.confsched2018.di.Injectable import io.github.droidkaigi.confsched2018.model.Session import io.github.droidkaigi.confsched2018.presentation.NavigationController import io.github.droidkaigi.confsched2018.presentation.Result +import io.github.droidkaigi.confsched2018.presentation.common.pref.PreviousSessionPrefs import io.github.droidkaigi.confsched2018.presentation.common.view.OnTabReselectedListener import io.github.droidkaigi.confsched2018.presentation.sessions.SessionsFragment.CurrentSessionScroller +import io.github.droidkaigi.confsched2018.presentation.sessions.SessionsFragment.SavePreviousSessionScroller import io.github.droidkaigi.confsched2018.presentation.sessions.item.DateSessionsSection import io.github.droidkaigi.confsched2018.presentation.sessions.item.SpeechSessionItem import io.github.droidkaigi.confsched2018.util.ProgressTimeLatch import io.github.droidkaigi.confsched2018.util.SessionAlarm import io.github.droidkaigi.confsched2018.util.ext.addOnScrollListener +import io.github.droidkaigi.confsched2018.util.ext.getScrollState import io.github.droidkaigi.confsched2018.util.ext.isGone import io.github.droidkaigi.confsched2018.util.ext.observe +import io.github.droidkaigi.confsched2018.util.ext.restoreScrollState import io.github.droidkaigi.confsched2018.util.ext.setLinearDivider import io.github.droidkaigi.confsched2018.util.ext.setTextIfChanged import io.github.droidkaigi.confsched2018.util.ext.setVisible @@ -42,7 +46,8 @@ class AllSessionsFragment : Fragment(), Injectable, CurrentSessionScroller, - OnTabReselectedListener { + OnTabReselectedListener, + SavePreviousSessionScroller { private lateinit var binding: FragmentAllSessionsBinding @@ -86,7 +91,10 @@ class AllSessionsFragment : sessionsSection.updateSessions(sessions, onFavoriteClickListener, onFeedbackListener, true) - sessionsViewModel.onSuccessFetchSessions() + sessionsViewModel.onShowSessions() + if (sessionsViewModel.isNeedRestoreScrollState) { + scrollToPreviousSession() + } } is Result.Failure -> { Timber.e(result.e) @@ -113,6 +121,26 @@ class AllSessionsFragment : binding.sessionsRecycler.smoothScrollToPosition(0) } + override fun requestSavingScrollState() { + val layoutManager = binding.sessionsRecycler.layoutManager as LinearLayoutManager + PreviousSessionPrefs.scrollState = layoutManager.getScrollState() + } + + override fun requestRestoringScrollState() { + if (sessionsViewModel.sessions is Result.Success<*>) { + scrollToPreviousSession() + } else { + sessionsViewModel.isNeedRestoreScrollState = true + } + } + + private fun scrollToPreviousSession() { + sessionsViewModel.isNeedRestoreScrollState = false + val layoutManager = binding.sessionsRecycler.layoutManager as LinearLayoutManager + layoutManager.restoreScrollState(PreviousSessionPrefs.scrollState) + PreviousSessionPrefs.initPreviousSessionPrefs() + } + private fun setupRecyclerView() { val groupAdapter = GroupAdapter().apply { add(sessionsSection) diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsViewModel.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsViewModel.kt index 05634bc3..3b6b31ce 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsViewModel.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsViewModel.kt @@ -26,6 +26,8 @@ class AllSessionsViewModel @Inject constructor( private val focusCurrentSession: MutableLiveData = MutableLiveData() val refreshFocusCurrentSession: LiveData = focusCurrentSession + var isNeedRestoreScrollState: Boolean = false + val sessions: LiveData>> by lazy { repository.sessions .toResult(schedulerProvider) @@ -43,7 +45,7 @@ class AllSessionsViewModel @Inject constructor( .addTo(compositeDisposable) } - fun onSuccessFetchSessions() { + fun onShowSessions() { refreshFocusCurrentSession() } diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsFragment.kt index 3435ff9f..75c9891f 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsFragment.kt @@ -21,15 +21,19 @@ import io.github.droidkaigi.confsched2018.model.Room import io.github.droidkaigi.confsched2018.model.Session import io.github.droidkaigi.confsched2018.presentation.NavigationController import io.github.droidkaigi.confsched2018.presentation.Result +import io.github.droidkaigi.confsched2018.presentation.common.pref.PreviousSessionPrefs import io.github.droidkaigi.confsched2018.presentation.common.view.OnTabReselectedListener import io.github.droidkaigi.confsched2018.presentation.sessions.SessionsFragment.CurrentSessionScroller +import io.github.droidkaigi.confsched2018.presentation.sessions.SessionsFragment.SavePreviousSessionScroller import io.github.droidkaigi.confsched2018.presentation.sessions.item.DateSessionsSection import io.github.droidkaigi.confsched2018.presentation.sessions.item.SpeechSessionItem import io.github.droidkaigi.confsched2018.util.ProgressTimeLatch import io.github.droidkaigi.confsched2018.util.SessionAlarm import io.github.droidkaigi.confsched2018.util.ext.addOnScrollListener +import io.github.droidkaigi.confsched2018.util.ext.getScrollState import io.github.droidkaigi.confsched2018.util.ext.isGone import io.github.droidkaigi.confsched2018.util.ext.observe +import io.github.droidkaigi.confsched2018.util.ext.restoreScrollState import io.github.droidkaigi.confsched2018.util.ext.setLinearDivider import io.github.droidkaigi.confsched2018.util.ext.setTextIfChanged import io.github.droidkaigi.confsched2018.util.ext.setVisible @@ -43,7 +47,8 @@ class RoomSessionsFragment : Fragment(), Injectable, CurrentSessionScroller, - OnTabReselectedListener { + OnTabReselectedListener, + SavePreviousSessionScroller { private lateinit var binding: FragmentRoomSessionsBinding private lateinit var roomName: String @@ -94,7 +99,10 @@ class RoomSessionsFragment : sessionsSection.updateSessions(sessions, onFavoriteClickListener, onFeedbackListener, true) - sessionsViewModel.onSuccessFetchSessions() + sessionsViewModel.onShowSessions() + if (sessionsViewModel.isNeedRestoreScrollState) { + scrollToPreviousSession() + } } is Result.Failure -> { Timber.e(result.e) @@ -121,6 +129,26 @@ class RoomSessionsFragment : binding.sessionsRecycler.smoothScrollToPosition(0) } + override fun requestSavingScrollState() { + val layoutManager = binding.sessionsRecycler.layoutManager as LinearLayoutManager + PreviousSessionPrefs.scrollState = layoutManager.getScrollState() + } + + override fun requestRestoringScrollState() { + if (sessionsViewModel.sessions is Result.Success<*>) { + scrollToPreviousSession() + } else { + sessionsViewModel.isNeedRestoreScrollState = true + } + } + + private fun scrollToPreviousSession() { + sessionsViewModel.isNeedRestoreScrollState = false + val layoutManager = binding.sessionsRecycler.layoutManager as LinearLayoutManager + layoutManager.restoreScrollState(PreviousSessionPrefs.scrollState) + PreviousSessionPrefs.initPreviousSessionPrefs() + } + private fun setupRecyclerView() { val groupAdapter = GroupAdapter().apply { add(sessionsSection) diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsViewModel.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsViewModel.kt index 4eda8ccb..751c0863 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsViewModel.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsViewModel.kt @@ -26,6 +26,9 @@ class RoomSessionsViewModel @Inject constructor( private val focusCurrentSession: MutableLiveData = MutableLiveData() val refreshFocusCurrentSession: LiveData = focusCurrentSession + + var isNeedRestoreScrollState: Boolean = false + lateinit var roomName: String val sessions: LiveData>> by lazy { @@ -47,7 +50,7 @@ class RoomSessionsViewModel @Inject constructor( .addTo(compositeDisposable) } - fun onSuccessFetchSessions() { + fun onShowSessions() { refreshFocusCurrentSession() } diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/ScheduleSessionsFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/ScheduleSessionsFragment.kt index 665267a4..73e3133b 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/ScheduleSessionsFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/ScheduleSessionsFragment.kt @@ -18,17 +18,25 @@ import io.github.droidkaigi.confsched2018.model.Session import io.github.droidkaigi.confsched2018.model.SessionSchedule import io.github.droidkaigi.confsched2018.presentation.NavigationController import io.github.droidkaigi.confsched2018.presentation.Result +import io.github.droidkaigi.confsched2018.presentation.common.pref.PreviousSessionPrefs import io.github.droidkaigi.confsched2018.presentation.common.view.OnTabReselectedListener +import io.github.droidkaigi.confsched2018.presentation.sessions.SessionsFragment.SavePreviousSessionScroller import io.github.droidkaigi.confsched2018.presentation.sessions.item.ScheduleSessionsSection import io.github.droidkaigi.confsched2018.presentation.sessions.item.SpeechSessionItem import io.github.droidkaigi.confsched2018.util.ProgressTimeLatch import io.github.droidkaigi.confsched2018.util.SessionAlarm +import io.github.droidkaigi.confsched2018.util.ext.getScrollState import io.github.droidkaigi.confsched2018.util.ext.observe +import io.github.droidkaigi.confsched2018.util.ext.restoreScrollState import io.github.droidkaigi.confsched2018.util.ext.setLinearDivider import timber.log.Timber import javax.inject.Inject -class ScheduleSessionsFragment : Fragment(), Injectable, OnTabReselectedListener { +class ScheduleSessionsFragment : + Fragment(), + Injectable, + OnTabReselectedListener, + SavePreviousSessionScroller { private lateinit var binding: FragmentScheduleSessionsBinding @@ -78,6 +86,9 @@ class ScheduleSessionsFragment : Fragment(), Injectable, OnTabReselectedListener val sessions = result.data sessionsSection.updateSessions(sessions, onFavoriteClickListener, onFeedbackListener) + if (scheduleSessionsViewModel.isNeedRestoreScrollState) { + scrollToPreviousSession() + } } is Result.Failure -> { Timber.e(result.e) @@ -109,6 +120,26 @@ class ScheduleSessionsFragment : Fragment(), Injectable, OnTabReselectedListener binding.sessionsRecycler.smoothScrollToPosition(0) } + override fun requestSavingScrollState() { + val layoutManager = binding.sessionsRecycler.layoutManager as LinearLayoutManager + PreviousSessionPrefs.scrollState = layoutManager.getScrollState() + } + + override fun requestRestoringScrollState() { + if (scheduleSessionsViewModel.sessions is Result.Success<*>) { + scrollToPreviousSession() + } else { + scheduleSessionsViewModel.isNeedRestoreScrollState = true + } + } + + private fun scrollToPreviousSession() { + scheduleSessionsViewModel.isNeedRestoreScrollState = false + val layoutManager = binding.sessionsRecycler.layoutManager as LinearLayoutManager + layoutManager.restoreScrollState(PreviousSessionPrefs.scrollState) + PreviousSessionPrefs.initPreviousSessionPrefs() + } + companion object { private const val ARG_SCHEDULE = "schedule" diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/ScheduleSessionsViewModel.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/ScheduleSessionsViewModel.kt index 6ab4973d..e4e79689 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/ScheduleSessionsViewModel.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/ScheduleSessionsViewModel.kt @@ -25,6 +25,8 @@ class ScheduleSessionsViewModel @Inject constructor( lateinit var schedule: SessionSchedule + var isNeedRestoreScrollState: Boolean = false + val sessions: LiveData>> by lazy { repository.scheduleSessions .map { t: Map> -> diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/SessionsFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/SessionsFragment.kt index 13619f33..ce54b37a 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/SessionsFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/SessionsFragment.kt @@ -27,6 +27,7 @@ import io.github.droidkaigi.confsched2018.presentation.MainActivity.BottomNaviga import io.github.droidkaigi.confsched2018.presentation.Result import io.github.droidkaigi.confsched2018.presentation.common.fragment.Findable import io.github.droidkaigi.confsched2018.presentation.common.pref.Prefs +import io.github.droidkaigi.confsched2018.presentation.common.pref.PreviousSessionPrefs import io.github.droidkaigi.confsched2018.presentation.common.view.OnTabReselectedDispatcher import io.github.droidkaigi.confsched2018.util.ProgressTimeLatch import io.github.droidkaigi.confsched2018.util.ext.observe @@ -110,7 +111,7 @@ class SessionsFragment : Fragment(), Injectable, Findable, OnReselectedListener .get(SessionsViewModel::class.java) if (Prefs.enableReopenPreviousRoomSessions and (savedInstanceState == null)) { - sessionsViewModel.changeTabMode(Prefs.previousSessionTabMode) + sessionsViewModel.changeTabMode(PreviousSessionPrefs.previousSessionTabMode) } val progressTimeLatch = ProgressTimeLatch { @@ -159,14 +160,19 @@ class SessionsFragment : Fragment(), Injectable, Findable, OnReselectedListener super.onPause() when (Prefs.enableReopenPreviousRoomSessions) { true -> saveCurrentSession() - false -> Prefs.initPreviousSessionPrefs() + false -> PreviousSessionPrefs.initPreviousSessionPrefs() } } private fun saveCurrentSession() { val currentItem = binding.sessionsViewPager.currentItem - Prefs.previousSessionTabId = currentItem - Prefs.previousSessionTabMode = sessionsViewModel.tabMode + PreviousSessionPrefs.previousSessionTabId = currentItem + PreviousSessionPrefs.previousSessionTabMode = sessionsViewModel.tabMode + val fragment = sessionsViewPagerAdapter + .instantiateItem(binding.sessionsViewPager, currentItem) + if (fragment is SavePreviousSessionScroller) { + fragment.requestSavingScrollState() + } } override fun onReselected() { @@ -188,11 +194,15 @@ class SessionsFragment : Fragment(), Injectable, Findable, OnReselectedListener } private fun reopenPreviousOpenedItem() { - val previousItem = Prefs.previousSessionTabId + val previousItem = PreviousSessionPrefs.previousSessionTabId if (previousItem < 0) return binding.sessionsViewPager.currentItem = previousItem - Prefs.initPreviousSessionPrefs() + val fragment = sessionsViewPagerAdapter + .instantiateItem(binding.sessionsViewPager, previousItem) + if (fragment is SavePreviousSessionScroller) { + fragment.requestRestoringScrollState() + } } override val tagForFinding = MainActivity.BottomNavigationItem.SESSION.name @@ -201,6 +211,11 @@ class SessionsFragment : Fragment(), Injectable, Findable, OnReselectedListener fun scrollToCurrentSession() } + interface SavePreviousSessionScroller { + fun requestSavingScrollState() + fun requestRestoringScrollState() + } + companion object { fun newInstance(): SessionsFragment = SessionsFragment() } diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/util/ext/LinearLayoutManagerExt.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/util/ext/LinearLayoutManagerExt.kt new file mode 100644 index 00000000..913bab45 --- /dev/null +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/util/ext/LinearLayoutManagerExt.kt @@ -0,0 +1,37 @@ +package io.github.droidkaigi.confsched2018.util.ext + +import android.os.Parcel +import android.os.Parcelable +import android.support.v7.widget.LinearLayoutManager + +fun LinearLayoutManager.getScrollState(): ScrollState { + val savedState = onSaveInstanceState() + val parcel = Parcel.obtain() + savedState.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE) + parcel.setDataPosition(0) + + val scrollState = ScrollState( + anchorPosition = parcel.readInt(), + anchorOffset = parcel.readInt()) + parcel.recycle() + + return scrollState +} + +fun LinearLayoutManager.restoreScrollState(scrollState: ScrollState) { + if (scrollState.anchorPosition < 0) return + + val parcel = Parcel.obtain() + parcel.writeInt(scrollState.anchorPosition) + parcel.writeInt(scrollState.anchorOffset) + parcel.writeInt(0) + parcel.setDataPosition(0) + + val savedState = LinearLayoutManager.SavedState.CREATOR.createFromParcel(parcel) + + parcel.recycle() + + onRestoreInstanceState(savedState) +} + +class ScrollState(val anchorPosition: Int, val anchorOffset: Int) diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml index afc80e3e..5bded1cc 100644 --- a/app/src/main/res/values/integers.xml +++ b/app/src/main/res/values/integers.xml @@ -2,4 +2,6 @@ 5 -1 + -1 + 0