From f751b98a586ac8adc75d927e327d8117b322bb5a Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Sat, 17 Feb 2018 12:51:22 +0100 Subject: [PATCH] =?UTF-8?q?Improved=20message=20view=20-=20fixed=20scroll?= =?UTF-8?q?=20behaviour=20-=20use=20Paging=E2=80=99s=20BoundaryCallback=20?= =?UTF-8?q?for=20loadMore=20detection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 6 +- .../persistence/AccountDatabase.kt | 4 +- .../persistence/QuasselDatabase.kt | 6 +- .../quasseldroid_ng/ui/chat/MessageAdapter.kt | 4 - .../ui/chat/MessageListFragment.kt | 94 +++++++++++-------- .../ui/setup/accounts/AccountViewModel.kt | 10 +- app/src/main/res/layout/fragment_messages.xml | 1 + 7 files changed, 68 insertions(+), 57 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7f0c3f784..c96cc3e24 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -106,9 +106,9 @@ dependencies { implementation(appArch("lifecycle", "reactivestreams")) kapt(appArch("lifecycle", "compiler")) - implementation(appArch("persistence.room", "runtime", "1.0.0")) - implementation(appArch("persistence.room", "rxjava2", "1.0.0")) - kapt(appArch("persistence.room", "compiler", "1.0.0")) + implementation(appArch("persistence.room", "runtime", "1.1.0-alpha2")) + implementation(appArch("persistence.room", "rxjava2", "1.1.0-alpha2")) + kapt(appArch("persistence.room", "compiler", "1.1.0-alpha2")) implementation(appArch("paging", "runtime", version = "1.0.0-alpha5")) { exclude(group = "junit", module = "junit") diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/AccountDatabase.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/AccountDatabase.kt index d5429e045..ebe1e6100 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/AccountDatabase.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/AccountDatabase.kt @@ -1,6 +1,6 @@ package de.kuschku.quasseldroid_ng.persistence -import android.arch.paging.LivePagedListProvider +import android.arch.paging.DataSource import android.arch.persistence.room.* import android.content.Context @@ -32,7 +32,7 @@ abstract class AccountDatabase : RoomDatabase() { fun findById(id: Long): AccountDatabase.Account? @Query("SELECT * FROM account ORDER BY lastUsed DESC") - fun all(): LivePagedListProvider<Int, Account> + fun all(): DataSource.Factory<Int, Account> @Delete fun delete(account: AccountDatabase.Account) diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt index 714246e92..3f91598da 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt @@ -1,6 +1,6 @@ package de.kuschku.quasseldroid_ng.persistence -import android.arch.paging.LivePagedListProvider +import android.arch.paging.DataSource import android.arch.persistence.room.* import android.content.Context import android.support.annotation.IntRange @@ -60,8 +60,8 @@ abstract class QuasselDatabase : RoomDatabase() { @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC") fun findByBufferId(bufferId: Int): List<DatabaseMessage> - @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC") - fun findByBufferIdPaged(bufferId: Int): LivePagedListProvider<Int, DatabaseMessage> + @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC") + fun findByBufferIdPaged(bufferId: Int): DataSource.Factory<Int, DatabaseMessage> @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1") fun findLastByBufferId(bufferId: Int): DatabaseMessage? diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageAdapter.kt index 28720cc90..85428e267 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageAdapter.kt @@ -21,10 +21,6 @@ class MessageAdapter(context: Context) : private val messageCache = LruCache<Int, FormattedMessage>(512) - init { - setHasStableIds(true) - } - override fun onBindViewHolder(holder: QuasselMessageViewHolder, position: Int) { getItem(position)?.let { messageRenderer.bind( diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageListFragment.kt index 1150ac143..a71fe9000 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/MessageListFragment.kt @@ -3,6 +3,7 @@ package de.kuschku.quasseldroid_ng.ui.chat import android.arch.lifecycle.LiveData import android.arch.lifecycle.MutableLiveData import android.arch.lifecycle.Observer +import android.arch.paging.LivePagedListBuilder import android.arch.paging.PagedList import android.os.Bundle import android.support.design.widget.FloatingActionButton @@ -14,13 +15,10 @@ import android.view.ViewGroup import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.protocol.BufferId -import de.kuschku.libquassel.session.Backend -import de.kuschku.libquassel.session.SessionManager import de.kuschku.quasseldroid_ng.R import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread import de.kuschku.quasseldroid_ng.util.helper.invoke -import de.kuschku.quasseldroid_ng.util.helper.map import de.kuschku.quasseldroid_ng.util.helper.switchMap import de.kuschku.quasseldroid_ng.util.helper.toggle import de.kuschku.quasseldroid_ng.util.service.ServiceBoundFragment @@ -39,37 +37,70 @@ class MessageListFragment : ServiceBoundFragment() { @BindView(R.id.scrollDown) lateinit var scrollDown: FloatingActionButton - private val sessionManager: LiveData<SessionManager?> = backend.map(Backend::sessionManager) - override fun onCreate(savedInstanceState: Bundle?) { handler.onCreate() super.onCreate(savedInstanceState) setHasOptionsMenu(true) } + private val boundaryCallback = object : + PagedList.BoundaryCallback<QuasselDatabase.DatabaseMessage>() { + override fun onItemAtFrontLoaded(itemAtFront: QuasselDatabase.DatabaseMessage) + = Unit + + override fun onItemAtEndLoaded(itemAtEnd: QuasselDatabase.DatabaseMessage) + = loadMore() + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_messages, container, false) ButterKnife.bind(this, view) + val adapter = MessageAdapter(context!!) + + messageList.adapter = adapter + val linearLayoutManager = LinearLayoutManager(context) + linearLayoutManager.reverseLayout = true + messageList.layoutManager = linearLayoutManager + + messageList.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + val canScrollDown = recyclerView.canScrollVertically(1) + val isScrollingDown = dy > 0 + + scrollDown.visibility = View.VISIBLE + scrollDown.toggle(canScrollDown && isScrollingDown) + } + } + ) + database = QuasselDatabase.Creator.init(context!!.applicationContext) val data = buffer.switchMap { - database.message().findByBufferIdPaged(it).create( - Int.MAX_VALUE, - PagedList.Config.Builder() - .setPageSize(50) - .setEnablePlaceholders(false) - .setPrefetchDistance(50) - .build() - ) + LivePagedListBuilder(database.message().findByBufferIdPaged(it), 20) + .setBoundaryCallback(boundaryCallback) + .setInitialLoadKey(null) + .build() } - val adapter = MessageAdapter(context!!) - data.observe( this, Observer { list -> + val findFirstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition() adapter.setList(list) + if (findFirstVisibleItemPosition < 2) { + activity?.runOnUiThread { + messageList.smoothScrollToPosition(0) + } + handler.postDelayed( + { + activity?.runOnUiThread { + messageList.smoothScrollToPosition(0) + } + }, 16 + ) + } } ) @@ -77,36 +108,23 @@ class MessageListFragment : ServiceBoundFragment() { this, Observer { handler.post { // Try loading messages when switching to isEmpty buffer - if (it != null && database.message().bufferSize(it) == 0) { - loadMore() + if (it != null) { + if (database.message().bufferSize(it) == 0) { + loadMore() + } + activity?.runOnUiThread { + messageList.scrollToPosition(0) + } } } } ) - var recyclerViewMeasuredHeight = 0 - val scrollDownListener = object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - if (!recyclerView.canScrollVertically(-1)) { - loadMore() - } - if (recyclerViewMeasuredHeight == 0) - recyclerViewMeasuredHeight = recyclerView.measuredHeight - val canScrollDown = recyclerView.canScrollVertically(1) - val isScrollingDown = dy > 0 - val scrollOffsetFromBottom = recyclerView.computeVerticalScrollRange() - recyclerView.computeVerticalScrollOffset() - recyclerViewMeasuredHeight - val isMoreThanOneScreenFromBottom = scrollOffsetFromBottom > recyclerViewMeasuredHeight - val smartVisibility = scrollDown.visibility == View.VISIBLE || isMoreThanOneScreenFromBottom - scrollDown.toggle(canScrollDown && isScrollingDown && smartVisibility) - } + scrollDown.hide() + scrollDown.setOnClickListener { + messageList.scrollToPosition(0) } - messageList.adapter = adapter - messageList.layoutManager = LinearLayoutManager(context) - messageList.addOnScrollListener(scrollDownListener) - - scrollDown.setOnClickListener { messageList.scrollToPosition(adapter.itemCount) } - return view } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountViewModel.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountViewModel.kt index cf2aca05e..d873980ca 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountViewModel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/accounts/AccountViewModel.kt @@ -3,6 +3,7 @@ package de.kuschku.quasseldroid_ng.ui.setup.accounts import android.app.Application import android.arch.lifecycle.AndroidViewModel import android.arch.lifecycle.LiveData +import android.arch.paging.LivePagedListBuilder import android.arch.paging.PagedList import de.kuschku.quasseldroid_ng.persistence.AccountDatabase @@ -10,11 +11,6 @@ class AccountViewModel(application: Application) : AndroidViewModel(application) private val database: AccountDatabase = AccountDatabase.Creator.init( getApplication() ) - val accounts: LiveData<PagedList<AccountDatabase.Account>> = database.accounts().all().create( - 0, - PagedList.Config.Builder() - .setPageSize(50) - .setPrefetchDistance(50) - .build() - ) + val accounts: LiveData<PagedList<AccountDatabase.Account>> + = LivePagedListBuilder(database.accounts().all(), 20).build() } diff --git a/app/src/main/res/layout/fragment_messages.xml b/app/src/main/res/layout/fragment_messages.xml index 2470d8d7d..e8323981d 100644 --- a/app/src/main/res/layout/fragment_messages.xml +++ b/app/src/main/res/layout/fragment_messages.xml @@ -24,6 +24,7 @@ android:layout_marginEnd="12dp" android:layout_marginRight="12dp" android:tint="@color/colorFillDark" + android:visibility="gone" app:backgroundTint="#8A808080" app:elevation="0dip" app:fabSize="mini" -- GitLab