mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-27 08:55:54 +03:00
Load shared items while scrolling
Signed-off-by: Tim Krüger <t@timkrueger.me>
This commit is contained in:
parent
d92f5546e9
commit
a322a2ad73
6 changed files with 148 additions and 106 deletions
|
@ -8,6 +8,7 @@ import androidx.core.content.res.ResourcesCompat
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.adapters.SharedItemsAdapter
|
||||
|
@ -60,7 +61,7 @@ class SharedItemsActivity : AppCompatActivity() {
|
|||
SharedItemsViewModel.Factory(userEntity, roomToken, currentTab)
|
||||
).get(SharedItemsViewModel::class.java)
|
||||
|
||||
viewModel.media.observe(this) {
|
||||
viewModel.sharedItems.observe(this) {
|
||||
Log.d(TAG, "Items received: $it")
|
||||
|
||||
if (currentTab == TAB_MEDIA) {
|
||||
|
@ -71,8 +72,6 @@ class SharedItemsActivity : AppCompatActivity() {
|
|||
|
||||
val layoutManager = GridLayoutManager(this, 4)
|
||||
binding.imageRecycler.layoutManager = layoutManager
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
} else {
|
||||
val adapter = SharedItemsListAdapter()
|
||||
adapter.items = it.items
|
||||
|
@ -82,15 +81,22 @@ class SharedItemsActivity : AppCompatActivity() {
|
|||
val layoutManager = LinearLayoutManager(this)
|
||||
layoutManager.orientation = LinearLayoutManager.VERTICAL
|
||||
binding.imageRecycler.layoutManager = layoutManager
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
binding.imageRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (!recyclerView.canScrollVertically(1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
viewModel.loadNextItems()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun updateItems(type: String) {
|
||||
currentTab = type
|
||||
viewModel.loadMediaItems(type)
|
||||
viewModel.loadItems(type)
|
||||
}
|
||||
|
||||
private fun initTabs() {
|
||||
|
@ -134,13 +140,9 @@ class SharedItemsActivity : AppCompatActivity() {
|
|||
updateItems(tab.tag as String)
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) {
|
||||
// unused atm
|
||||
}
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) = Unit
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
// unused atm
|
||||
}
|
||||
override fun onTabReselected(tab: TabLayout.Tab) = Unit
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -29,12 +29,16 @@ class SharedItemsRepository {
|
|||
}
|
||||
|
||||
fun media(type: String): Observable<Response<ChatShareOverall>>? {
|
||||
return media(type, null)
|
||||
}
|
||||
|
||||
fun media(type: String, lastKnownMessageId: Int?): Observable<Response<ChatShareOverall>>? {
|
||||
val credentials = ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken)
|
||||
|
||||
return ncApi.getSharedItems(
|
||||
credentials,
|
||||
ApiUtils.getUrlForChatSharedItems(1, parameters!!.baseUrl, parameters!!.roomToken),
|
||||
type, null, null
|
||||
type, lastKnownMessageId, 28
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.nextcloud.talk.repositories
|
||||
|
||||
class SharedMediaItems(
|
||||
val items: List<SharedItem>,
|
||||
val lastSeenId: String,
|
||||
val type: String,
|
||||
val items: MutableList<SharedItem>,
|
||||
var lastSeenId: Int?,
|
||||
var moreItemsExisting: Boolean,
|
||||
val authHeader: Map<String, String>
|
||||
)
|
||||
|
|
|
@ -16,76 +16,109 @@ import io.reactivex.disposables.Disposable
|
|||
import io.reactivex.schedulers.Schedulers
|
||||
import retrofit2.Response
|
||||
|
||||
class SharedItemsViewModel(private val repository: SharedItemsRepository, private val initialType: String) : ViewModel() {
|
||||
class SharedItemsViewModel(private val repository: SharedItemsRepository, private val initialType: String) :
|
||||
ViewModel() {
|
||||
|
||||
private val _media: MutableLiveData<SharedMediaItems> by lazy {
|
||||
private val _sharedItems: MutableLiveData<SharedMediaItems> by lazy {
|
||||
MutableLiveData<SharedMediaItems>().also {
|
||||
loadMediaItems(initialType)
|
||||
loadItems(initialType)
|
||||
}
|
||||
}
|
||||
|
||||
val media: LiveData<SharedMediaItems>
|
||||
get() = _media
|
||||
val sharedItems: LiveData<SharedMediaItems>
|
||||
get() = _sharedItems
|
||||
|
||||
fun loadMediaItems(type: String) {
|
||||
fun loadNextItems() {
|
||||
val currentSharedItems = sharedItems.value!!
|
||||
|
||||
if (currentSharedItems.moreItemsExisting) {
|
||||
repository.media(currentSharedItems.type, currentSharedItems.lastSeenId)?.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(observer(currentSharedItems.type, false))
|
||||
}
|
||||
}
|
||||
|
||||
fun loadItems(type: String) {
|
||||
repository.media(type)?.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(object : Observer<Response<ChatShareOverall>> {
|
||||
?.subscribe(observer(type, true))
|
||||
}
|
||||
|
||||
var chatLastGiven: String = ""
|
||||
val items = mutableMapOf<String, SharedItem>()
|
||||
private fun observer(type: String, initModel: Boolean): Observer<Response<ChatShareOverall>> {
|
||||
return object : Observer<Response<ChatShareOverall>> {
|
||||
|
||||
override fun onSubscribe(d: Disposable) = Unit
|
||||
var chatLastGiven: Int? = null
|
||||
val items = mutableMapOf<String, SharedItem>()
|
||||
|
||||
override fun onNext(response: Response<ChatShareOverall>) {
|
||||
override fun onSubscribe(d: Disposable) = Unit
|
||||
|
||||
if (response.headers()["x-chat-last-given"] != null) {
|
||||
chatLastGiven = response.headers()["x-chat-last-given"]!!
|
||||
}
|
||||
override fun onNext(response: Response<ChatShareOverall>) {
|
||||
|
||||
val mediaItems = response.body()!!.ocs!!.data
|
||||
mediaItems?.forEach {
|
||||
if (it.value.messageParameters.containsKey("file")) {
|
||||
val fileParameters = it.value.messageParameters["file"]!!
|
||||
|
||||
val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true)
|
||||
|
||||
items[it.value.id] = SharedItem(
|
||||
fileParameters["id"]!!,
|
||||
fileParameters["name"]!!,
|
||||
fileParameters["size"]!!.toInt(),
|
||||
fileParameters["path"]!!,
|
||||
fileParameters["link"]!!,
|
||||
fileParameters["mimetype"]!!,
|
||||
previewAvailable,
|
||||
repository.previewLink(fileParameters["id"]),
|
||||
repository.parameters!!.userEntity
|
||||
)
|
||||
} else {
|
||||
Log.w(TAG, "location and deckcard are not yet supported")
|
||||
}
|
||||
|
||||
}
|
||||
if (response.headers()["x-chat-last-given"] != null) {
|
||||
chatLastGiven = response.headers()["x-chat-last-given"]!!.toInt()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.d(TAG, "An error occurred: $e")
|
||||
}
|
||||
val mediaItems = response.body()!!.ocs!!.data
|
||||
mediaItems?.forEach {
|
||||
if (it.value.messageParameters.containsKey("file")) {
|
||||
val fileParameters = it.value.messageParameters["file"]!!
|
||||
|
||||
override fun onComplete() {
|
||||
this@SharedItemsViewModel._media.value =
|
||||
val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true)
|
||||
|
||||
items[it.value.id] = SharedItem(
|
||||
fileParameters["id"]!!,
|
||||
fileParameters["name"]!!,
|
||||
fileParameters["size"]!!.toInt(),
|
||||
fileParameters["path"]!!,
|
||||
fileParameters["link"]!!,
|
||||
fileParameters["mimetype"]!!,
|
||||
previewAvailable,
|
||||
repository.previewLink(fileParameters["id"]),
|
||||
repository.parameters!!.userEntity
|
||||
)
|
||||
} else {
|
||||
Log.w(TAG, "location and deckcard are not yet supported")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.d(TAG, "An error occurred: $e")
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
|
||||
val sortedMutableItems = items.toSortedMap().values.toList().reversed().toMutableList()
|
||||
val moreItemsExisting = items.count() == 28
|
||||
|
||||
if (initModel) {
|
||||
this@SharedItemsViewModel._sharedItems.value =
|
||||
SharedMediaItems(
|
||||
items.toSortedMap().values.toList().reversed(),
|
||||
type,
|
||||
sortedMutableItems,
|
||||
chatLastGiven,
|
||||
moreItemsExisting,
|
||||
repository.authHeader()
|
||||
)
|
||||
} else {
|
||||
val oldItems = this@SharedItemsViewModel._sharedItems.value!!.items
|
||||
this@SharedItemsViewModel._sharedItems.value =
|
||||
SharedMediaItems(
|
||||
type,
|
||||
(oldItems.toMutableList() + sortedMutableItems) as MutableList<SharedItem>,
|
||||
chatLastGiven,
|
||||
moreItemsExisting,
|
||||
repository.authHeader()
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(val userEntity: UserEntity, val roomToken: String, private val initialType: String) : ViewModelProvider
|
||||
.Factory {
|
||||
class Factory(val userEntity: UserEntity, val roomToken: String, private val initialType: String) :
|
||||
ViewModelProvider
|
||||
.Factory {
|
||||
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(SharedItemsViewModel::class.java)) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Tim Krüger
|
||||
|
@ -34,12 +33,12 @@
|
|||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/appbar"
|
||||
android:theme="?attr/actionBarPopupTheme"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_scrollFlags="enterAlwaysCollapsed|noScroll"
|
||||
app:navigationIconTint="@color/fontAppbar"
|
||||
app:popupTheme="@style/appActionBarPopupMenu"
|
||||
app:titleTextColor="@color/fontAppbar"
|
||||
tools:title="@string/nc_app_product_name"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
tools:title="@string/nc_app_product_name" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/shared_items_tabs"
|
||||
|
@ -54,18 +53,15 @@
|
|||
app:tabMode="scrollable"
|
||||
app:tabTextAppearance="@style/TextAppearanceTab" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/nestedScrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/shared_items_tabs">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/image_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/attachment_item" />
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/image_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/shared_items_tabs"
|
||||
tools:listitem="@layout/attachment_item" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -20,40 +20,45 @@
|
|||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/preview_container"
|
||||
xmlns:fresco="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_alignSelf="flex_start"
|
||||
app:layout_flexGrow="1"
|
||||
app:layout_wrapBefore="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
xmlns:fresco="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/image"
|
||||
<FrameLayout
|
||||
android:id="@+id/preview_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_mimetype_file"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_alignSelf="flex_start"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:placeholderImageScaleType="fitCenter"
|
||||
fresco:actualImageScaleType="centerCrop"
|
||||
fresco:failureImage="@drawable/ic_mimetype_file"
|
||||
fresco:placeholderImage="@drawable/ic_mimetype_file"
|
||||
fresco:roundedCornerRadius="4dp"
|
||||
tools:src="@drawable/ic_call_black_24dp"/>
|
||||
app:layout_flexGrow="1"
|
||||
app:layout_wrapBefore="true">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_mimetype_file"
|
||||
app:placeholderImageScaleType="fitCenter"
|
||||
fresco:actualImageScaleType="centerCrop"
|
||||
fresco:failureImage="@drawable/ic_mimetype_file"
|
||||
fresco:placeholderImage="@drawable/ic_mimetype_file"
|
||||
fresco:roundedCornerRadius="4dp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in a new issue