diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e2f11f867..41ae4ce7f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -169,7 +169,7 @@
android:windowSoftInputMode="stateHidden" />
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt
deleted file mode 100644
index 240842254..000000000
--- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.nextcloud.talk.adapters
-
-import android.net.Uri
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.core.content.ContextCompat
-import androidx.recyclerview.widget.RecyclerView
-import com.facebook.drawee.backends.pipeline.Fresco
-import com.facebook.drawee.controller.BaseControllerListener
-import com.facebook.drawee.controller.ControllerListener
-import com.facebook.drawee.interfaces.DraweeController
-import com.facebook.drawee.view.SimpleDraweeView
-import com.facebook.imagepipeline.common.RotationOptions
-import com.facebook.imagepipeline.image.ImageInfo
-import com.facebook.imagepipeline.request.ImageRequestBuilder
-import com.nextcloud.talk.databinding.SharedItemGridBinding
-import com.nextcloud.talk.repositories.SharedItem
-import com.nextcloud.talk.utils.DrawableUtils
-import com.nextcloud.talk.utils.FileViewerUtils
-
-class SharedItemsGridAdapter : RecyclerView.Adapter() {
-
- companion object {
- private val TAG = SharedItemsGridAdapter::class.simpleName
- }
-
- class ViewHolder(val binding: SharedItemGridBinding, itemView: View) : RecyclerView.ViewHolder(itemView)
-
- var authHeader: Map = emptyMap()
- var items: List = emptyList()
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- val binding = SharedItemGridBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- return ViewHolder(binding, binding.root)
- }
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
-
- val currentItem = items[position]
-
- if (currentItem.previewAvailable == true) {
- val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(currentItem.previewLink))
- .setProgressiveRenderingEnabled(true)
- .setRotationOptions(RotationOptions.autoRotate())
- .disableDiskCache()
- .setHeaders(authHeader)
- .build()
-
- val listener: ControllerListener = object : BaseControllerListener() {
- override fun onFailure(id: String, e: Throwable) {
- Log.w(TAG, "Failed to load image. A static mimetype image will be used", e)
- setStaticMimetypeImage(currentItem, holder)
- }
- }
-
- val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
- .setOldController(holder.binding.image.controller)
- .setAutoPlayAnimations(true)
- .setImageRequest(imageRequest)
- .setControllerListener(listener)
- .build()
- holder.binding.image.controller = draweeController
- } else {
- setStaticMimetypeImage(currentItem, holder)
- }
-
- val fileViewerUtils = FileViewerUtils(holder.binding.image.context, currentItem.userEntity)
-
- holder.binding.image.setOnClickListener {
- fileViewerUtils.openFile(
- FileViewerUtils.FileInfo(currentItem.id, currentItem.name, currentItem.fileSize),
- currentItem.path,
- currentItem.link,
- currentItem.mimeType,
- FileViewerUtils.ProgressUi(
- holder.binding.progressBar,
- null,
- it as SimpleDraweeView
- )
- )
- }
-
- fileViewerUtils.resumeToUpdateViewsByProgress(
- currentItem.name,
- currentItem.id,
- currentItem.mimeType,
- FileViewerUtils.ProgressUi(holder.binding.progressBar, null, holder.binding.image)
- )
- }
-
- private fun setStaticMimetypeImage(
- currentItem: SharedItem,
- holder: ViewHolder
- ) {
- val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(currentItem.mimeType)
- val drawable = ContextCompat.getDrawable(holder.binding.image.context, drawableResourceId)
- holder.binding.image.hierarchy.setPlaceholderImage(drawable)
- }
-
- override fun getItemCount(): Int {
- return items.size
- }
-}
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt
deleted file mode 100644
index 56a084b21..000000000
--- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt
+++ /dev/null
@@ -1,119 +0,0 @@
-package com.nextcloud.talk.adapters
-
-import android.net.Uri
-import android.text.format.Formatter
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.core.content.ContextCompat
-import androidx.recyclerview.widget.RecyclerView
-import com.facebook.drawee.backends.pipeline.Fresco
-import com.facebook.drawee.controller.BaseControllerListener
-import com.facebook.drawee.controller.ControllerListener
-import com.facebook.drawee.interfaces.DraweeController
-import com.facebook.imagepipeline.common.RotationOptions
-import com.facebook.imagepipeline.image.ImageInfo
-import com.facebook.imagepipeline.request.ImageRequestBuilder
-import com.nextcloud.talk.databinding.SharedItemListBinding
-import com.nextcloud.talk.repositories.SharedItem
-import com.nextcloud.talk.utils.DateUtils
-import com.nextcloud.talk.utils.DrawableUtils
-import com.nextcloud.talk.utils.FileViewerUtils
-import com.nextcloud.talk.utils.FileViewerUtils.ProgressUi
-
-class SharedItemsListAdapter : RecyclerView.Adapter() {
-
- companion object {
- private val TAG = SharedItemsListAdapter::class.simpleName
- private const val ONE_SECOND_IN_MILLIS = 1000
- }
-
- class ViewHolder(val binding: SharedItemListBinding, itemView: View) : RecyclerView.ViewHolder(itemView)
-
- var authHeader: Map = emptyMap()
- var items: List = emptyList()
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- val binding = SharedItemListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- return ViewHolder(binding, binding.root)
- }
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
-
- val currentItem = items[position]
-
- holder.binding.fileName.text = currentItem.name
- holder.binding.fileSize.text = currentItem.fileSize?.let {
- Formatter.formatShortFileSize(
- holder.binding.fileSize.context,
- it
- )
- }
- holder.binding.fileDate.text = DateUtils.getLocalDateTimeStringFromTimestamp(
- currentItem.date * ONE_SECOND_IN_MILLIS
- )
-
- if (currentItem.previewAvailable == true) {
- val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(currentItem.previewLink))
- .setProgressiveRenderingEnabled(true)
- .setRotationOptions(RotationOptions.autoRotate())
- .disableDiskCache()
- .setHeaders(authHeader)
- .build()
-
- val listener: ControllerListener = object : BaseControllerListener() {
- override fun onFailure(id: String, e: Throwable) {
- Log.w(TAG, "Failed to load image. A static mimetype image will be used", e)
- setStaticMimetypeImage(currentItem, holder)
- }
- }
-
- val draweeController: DraweeController = Fresco.newDraweeControllerBuilder()
- .setOldController(holder.binding.fileImage.controller)
- .setAutoPlayAnimations(true)
- .setImageRequest(imageRequest)
- .setControllerListener(listener)
- .build()
- holder.binding.fileImage.controller = draweeController
- } else {
- setStaticMimetypeImage(currentItem, holder)
- }
-
- val fileViewerUtils = FileViewerUtils(holder.binding.fileImage.context, currentItem.userEntity)
-
- holder.binding.fileItem.setOnClickListener {
- fileViewerUtils.openFile(
- FileViewerUtils.FileInfo(currentItem.id, currentItem.name, currentItem.fileSize),
- currentItem.path,
- currentItem.link,
- currentItem.mimeType,
- ProgressUi(
- holder.binding.progressBar,
- null,
- holder.binding.fileImage
- )
- )
- }
-
- fileViewerUtils.resumeToUpdateViewsByProgress(
- currentItem.name,
- currentItem.id,
- currentItem.mimeType,
- ProgressUi(holder.binding.progressBar, null, holder.binding.fileImage)
- )
- }
-
- private fun setStaticMimetypeImage(
- currentItem: SharedItem,
- holder: ViewHolder
- ) {
- val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(currentItem.mimeType)
- val drawable = ContextCompat.getDrawable(holder.binding.fileImage.context, drawableResourceId)
- holder.binding.fileImage.hierarchy.setPlaceholderImage(drawable)
- }
-
- override fun getItemCount(): Int {
- return items.size
- }
-}
diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
index b11c8a60b..dc304b8db 100644
--- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
+++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
@@ -55,7 +55,9 @@ import com.nextcloud.talk.components.filebrowser.webdav.DavUtils
import com.nextcloud.talk.dagger.modules.BusModule
import com.nextcloud.talk.dagger.modules.ContextModule
import com.nextcloud.talk.dagger.modules.DatabaseModule
+import com.nextcloud.talk.dagger.modules.RepositoryModule
import com.nextcloud.talk.dagger.modules.RestModule
+import com.nextcloud.talk.dagger.modules.ViewModelModule
import com.nextcloud.talk.jobs.AccountRemovalWorker
import com.nextcloud.talk.jobs.CapabilitiesWorker
import com.nextcloud.talk.jobs.SignalingSettingsWorker
@@ -89,7 +91,9 @@ import javax.inject.Singleton
DatabaseModule::class,
RestModule::class,
UserModule::class,
- ArbitraryStorageModule::class
+ ArbitraryStorageModule::class,
+ ViewModelModule::class,
+ RepositoryModule::class
]
)
@Singleton
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
index e4714ea08..cfba72955 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
@@ -103,7 +103,7 @@ import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.CallActivity
import com.nextcloud.talk.activities.MainActivity
-import com.nextcloud.talk.activities.SharedItemsActivity
+import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.activities.TakePhotoActivity
import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder
import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt
index bc6c25a1e..7068af0c5 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt
@@ -51,7 +51,7 @@ import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.facebook.drawee.backends.pipeline.Fresco
import com.nextcloud.talk.R
-import com.nextcloud.talk.activities.SharedItemsActivity
+import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.adapters.items.ParticipantItem
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt
new file mode 100644
index 000000000..50da11477
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt
@@ -0,0 +1,36 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.dagger.modules
+
+import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.shareditems.repositories.SharedItemsRepository
+import com.nextcloud.talk.shareditems.repositories.SharedItemsRepositoryImpl
+import dagger.Module
+import dagger.Provides
+
+@Module
+class RepositoryModule {
+ @Provides
+ fun provideSharedItemsRepository(ncApi: NcApi): SharedItemsRepository {
+ return SharedItemsRepositoryImpl(ncApi)
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt
new file mode 100644
index 000000000..d684db1b2
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt
@@ -0,0 +1,56 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Nextcloud GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.dagger.modules
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel
+import dagger.Binds
+import dagger.MapKey
+import dagger.Module
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlin.reflect.KClass
+
+class ViewModelFactory @Inject constructor(
+ private val viewModels: MutableMap, Provider>
+) : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T = viewModels[modelClass]?.get() as T
+}
+
+@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
+@Retention(AnnotationRetention.RUNTIME)
+@MapKey
+internal annotation class ViewModelKey(val value: KClass)
+
+@Module
+abstract class ViewModelModule {
+
+ @Binds
+ abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
+
+ @Binds
+ @IntoMap
+ @ViewModelKey(SharedItemsViewModel::class)
+ abstract fun sharedItemsViewModel(viewModel: SharedItemsViewModel): ViewModel
+}
diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt
deleted file mode 100644
index 38aeef303..000000000
--- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.nextcloud.talk.repositories
-
-import com.nextcloud.talk.models.database.UserEntity
-
-data class SharedItem(
- val id: String,
- val name: String,
- val fileSize: Long?,
- val date: Long,
- val path: String,
- val link: String?,
- val mimeType: String?,
- val previewAvailable: Boolean?,
- val previewLink: String,
- val userEntity: UserEntity,
-)
diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt
deleted file mode 100644
index e263ae8cc..000000000
--- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.nextcloud.talk.repositories
-
-import java.util.Locale
-
-enum class SharedItemType {
-
- AUDIO,
- FILE,
- MEDIA,
- VOICE,
- LOCATION,
- DECKCARD,
- OTHER;
-
- companion object {
- fun typeFor(name: String) = valueOf(name.uppercase(Locale.ROOT))
- }
-}
diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt
deleted file mode 100644
index f34cd5497..000000000
--- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package com.nextcloud.talk.repositories
-
-import autodagger.AutoInjector
-import com.nextcloud.talk.R
-import com.nextcloud.talk.api.NcApi
-import com.nextcloud.talk.application.NextcloudTalkApplication
-import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
-import com.nextcloud.talk.models.database.UserEntity
-import com.nextcloud.talk.models.json.chat.ChatShareOverall
-import com.nextcloud.talk.models.json.chat.ChatShareOverviewOverall
-import com.nextcloud.talk.utils.ApiUtils
-import io.reactivex.Observable
-import retrofit2.Response
-import java.util.Locale
-import javax.inject.Inject
-
-@AutoInjector(NextcloudTalkApplication::class)
-class SharedItemsRepository {
-
- var parameters: Parameters? = null
-
- @Inject
- lateinit var ncApi: NcApi
-
- init {
- sharedApplication!!.componentApplication.inject(this)
- }
-
- fun media(type: SharedItemType): Observable>? {
- return media(type, null)
- }
-
- fun media(type: SharedItemType, lastKnownMessageId: Int?): Observable>? {
- val credentials = ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken)
-
- return ncApi.getSharedItems(
- credentials,
- ApiUtils.getUrlForChatSharedItems(1, parameters!!.baseUrl, parameters!!.roomToken),
- type.toString().lowercase(Locale.ROOT),
- lastKnownMessageId,
- BATCH_SIZE
- )
- }
-
- fun availableTypes(): Observable>? {
- val credentials = ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken)
-
- return ncApi.getSharedItemsOverview(
- credentials,
- ApiUtils.getUrlForChatSharedItemsOverview(1, parameters!!.baseUrl, parameters!!.roomToken),
- 1
- )
- }
-
- fun authHeader(): Map {
- return mapOf(Pair("Authorization", ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken)))
- }
-
- fun previewLink(fileId: String?): String {
- return ApiUtils.getUrlForFilePreviewWithFileId(
- parameters!!.baseUrl,
- fileId,
- sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size)
- )
- }
-
- data class Parameters(
- val userName: String,
- val userToken: String,
- val baseUrl: String,
- val userEntity: UserEntity,
- val roomToken: String
- )
-
- companion object {
- const val BATCH_SIZE: Int = 28
- }
-}
diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt
deleted file mode 100644
index 91f5fcf10..000000000
--- a/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.nextcloud.talk.repositories
-
-class SharedMediaItems(
- val type: SharedItemType,
- val items: MutableList,
- var lastSeenId: Int?,
- var moreItemsExisting: Boolean,
- val authHeader: Map
-)
diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/shareditems/activities/SharedItemsActivity.kt
similarity index 59%
rename from app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt
rename to app/src/main/java/com/nextcloud/talk/shareditems/activities/SharedItemsActivity.kt
index 76fb75600..a24a3d864 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/activities/SharedItemsActivity.kt
@@ -1,37 +1,64 @@
-package com.nextcloud.talk.activities
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.activities
import android.os.Bundle
import android.util.Log
import android.view.MenuItem
+import android.view.View
import androidx.appcompat.app.AppCompatActivity
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 autodagger.AutoInjector
import com.google.android.material.tabs.TabLayout
import com.nextcloud.talk.R
-import com.nextcloud.talk.adapters.SharedItemsGridAdapter
-import com.nextcloud.talk.adapters.SharedItemsListAdapter
+import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.databinding.ActivitySharedItemsBinding
import com.nextcloud.talk.models.database.UserEntity
-import com.nextcloud.talk.repositories.SharedItemType
+import com.nextcloud.talk.shareditems.adapters.SharedItemsAdapter
+import com.nextcloud.talk.shareditems.model.SharedItemType
+import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
-import com.nextcloud.talk.viewmodels.SharedItemsViewModel
+import javax.inject.Inject
+@AutoInjector(NextcloudTalkApplication::class)
class SharedItemsActivity : AppCompatActivity() {
+ @Inject
+ lateinit var viewModelFactory: ViewModelProvider.Factory
+
private lateinit var binding: ActivitySharedItemsBinding
private lateinit var viewModel: SharedItemsViewModel
- private lateinit var currentTab: SharedItemType
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
- currentTab = SharedItemType.MEDIA
+ NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
val roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!!
val conversationName = intent.getStringExtra(KEY_CONVERSATION_NAME)
@@ -55,35 +82,37 @@ class SharedItemsActivity : AppCompatActivity() {
supportActionBar?.title = conversationName
supportActionBar?.setDisplayHomeAsUpEnabled(true)
- viewModel = ViewModelProvider(
- this,
- SharedItemsViewModel.Factory(userEntity, roomToken, currentTab)
- ).get(SharedItemsViewModel::class.java)
+ viewModel = ViewModelProvider(this, viewModelFactory)[SharedItemsViewModel::class.java]
- viewModel.sharedItemType.observe(this) {
- initTabs(it)
- }
+ viewModel.viewState.observe(this) { state ->
+ clearEmptyLoading()
+ when (state) {
+ is SharedItemsViewModel.LoadingItemsState, SharedItemsViewModel.InitialState -> {
+ showLoading()
+ }
+ is SharedItemsViewModel.NoSharedItemsState -> {
+ showEmpty()
+ }
+ is SharedItemsViewModel.LoadedState -> {
+ val sharedMediaItems = state.items
+ Log.d(TAG, "Items received: $sharedMediaItems")
- viewModel.sharedItems.observe(this) {
- Log.d(TAG, "Items received: $it")
+ val showGrid = state.selectedType == SharedItemType.MEDIA
+ val layoutManager = if (showGrid) {
+ GridLayoutManager(this, SPAN_COUNT)
+ } else {
+ LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+ }
- if (currentTab == SharedItemType.MEDIA) {
- val adapter = SharedItemsGridAdapter()
- adapter.items = it.items
- adapter.authHeader = it.authHeader
- binding.imageRecycler.adapter = adapter
-
- val layoutManager = GridLayoutManager(this, SPAN_COUNT)
- binding.imageRecycler.layoutManager = layoutManager
- } else {
- val adapter = SharedItemsListAdapter()
- adapter.items = it.items
- adapter.authHeader = it.authHeader
- binding.imageRecycler.adapter = adapter
-
- val layoutManager = LinearLayoutManager(this)
- layoutManager.orientation = LinearLayoutManager.VERTICAL
- binding.imageRecycler.layoutManager = layoutManager
+ val adapter = SharedItemsAdapter(showGrid, userEntity).apply {
+ items = sharedMediaItems.items
+ }
+ binding.imageRecycler.adapter = adapter
+ binding.imageRecycler.layoutManager = layoutManager
+ }
+ is SharedItemsViewModel.TypesLoadedState -> {
+ initTabs(state.types)
+ }
}
}
@@ -95,15 +124,30 @@ class SharedItemsActivity : AppCompatActivity() {
}
}
})
+
+ viewModel.initialize(userEntity, roomToken)
}
- fun updateItems(type: SharedItemType) {
- currentTab = type
- viewModel.loadItems(type)
+ private fun clearEmptyLoading() {
+ binding.sharedItemsTabs.visibility = View.VISIBLE
+ binding.emptyContainer.emptyListView.visibility = View.GONE
+ }
+
+ private fun showLoading() {
+ binding.emptyContainer.emptyListViewHeadline.text = getString(R.string.file_list_loading)
+ binding.emptyContainer.emptyListView.visibility = View.VISIBLE
+ }
+
+ private fun showEmpty() {
+ binding.emptyContainer.emptyListViewHeadline.text = getString(R.string.nc_shared_items_empty)
+ binding.emptyContainer.emptyListView.visibility = View.VISIBLE
+ binding.sharedItemsTabs.visibility = View.GONE
}
private fun initTabs(sharedItemTypes: Set) {
+ binding.sharedItemsTabs.removeAllTabs()
+
if (sharedItemTypes.contains(SharedItemType.MEDIA)) {
val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab()
tabMedia.tag = SharedItemType.MEDIA
@@ -155,7 +199,7 @@ class SharedItemsActivity : AppCompatActivity() {
binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
- updateItems(tab.tag as SharedItemType)
+ viewModel.initialLoadItems(tab.tag as SharedItemType)
}
override fun onTabUnselected(tab: TabLayout.Tab) = Unit
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsAdapter.kt
new file mode 100644
index 000000000..63cab9b81
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsAdapter.kt
@@ -0,0 +1,70 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.adapters
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.nextcloud.talk.databinding.SharedItemGridBinding
+import com.nextcloud.talk.databinding.SharedItemListBinding
+import com.nextcloud.talk.models.database.UserEntity
+import com.nextcloud.talk.shareditems.model.SharedItem
+
+class SharedItemsAdapter(
+ private val showGrid: Boolean,
+ private val userEntity: UserEntity
+) : RecyclerView.Adapter() {
+
+ var items: List = emptyList()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SharedItemsViewHolder {
+
+ return if (showGrid) {
+ SharedItemsGridViewHolder(
+ SharedItemGridBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ ),
+ userEntity
+ )
+ } else {
+ SharedItemsListViewHolder(
+ SharedItemListBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ ),
+ userEntity
+ )
+ }
+ }
+
+ override fun onBindViewHolder(holder: SharedItemsViewHolder, position: Int) {
+ holder.onBind(items[position])
+ }
+
+ override fun getItemCount(): Int {
+ return items.size
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsGridViewHolder.kt b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsGridViewHolder.kt
new file mode 100644
index 000000000..b7bf7be6c
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsGridViewHolder.kt
@@ -0,0 +1,42 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.adapters
+
+import android.view.View
+import android.widget.ProgressBar
+import com.facebook.drawee.view.SimpleDraweeView
+import com.nextcloud.talk.databinding.SharedItemGridBinding
+import com.nextcloud.talk.models.database.UserEntity
+
+class SharedItemsGridViewHolder(
+ override val binding: SharedItemGridBinding,
+ userEntity: UserEntity
+) : SharedItemsViewHolder(binding, userEntity) {
+
+ override val image: SimpleDraweeView
+ get() = binding.image
+ override val clickTarget: View
+ get() = binding.image
+ override val progressBar: ProgressBar
+ get() = binding.progressBar
+}
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsListViewHolder.kt b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsListViewHolder.kt
new file mode 100644
index 000000000..7f5e4cbe7
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsListViewHolder.kt
@@ -0,0 +1,65 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.adapters
+
+import android.text.format.Formatter
+import android.view.View
+import android.widget.ProgressBar
+import com.facebook.drawee.view.SimpleDraweeView
+import com.nextcloud.talk.databinding.SharedItemListBinding
+import com.nextcloud.talk.models.database.UserEntity
+import com.nextcloud.talk.shareditems.model.SharedItem
+import com.nextcloud.talk.utils.DateUtils
+
+class SharedItemsListViewHolder(
+ override val binding: SharedItemListBinding,
+ userEntity: UserEntity
+) : SharedItemsViewHolder(binding, userEntity) {
+
+ override val image: SimpleDraweeView
+ get() = binding.fileImage
+ override val clickTarget: View
+ get() = binding.fileItem
+ override val progressBar: ProgressBar
+ get() = binding.progressBar
+
+ override fun onBind(item: SharedItem) {
+
+ super.onBind(item)
+
+ binding.fileName.text = item.name
+ binding.fileSize.text = item.fileSize?.let {
+ Formatter.formatShortFileSize(
+ binding.fileSize.context,
+ it
+ )
+ }
+ binding.fileDate.text = DateUtils.getLocalDateTimeStringFromTimestamp(
+ item.date * ONE_SECOND_IN_MILLIS
+ )
+ }
+
+ companion object {
+ private const val ONE_SECOND_IN_MILLIS = 1000
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt
new file mode 100644
index 000000000..397750dd8
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt
@@ -0,0 +1,133 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.adapters
+
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.util.Log
+import android.view.View
+import android.widget.ProgressBar
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+import com.facebook.drawee.backends.pipeline.Fresco
+import com.facebook.drawee.controller.BaseControllerListener
+import com.facebook.drawee.controller.ControllerListener
+import com.facebook.drawee.interfaces.DraweeController
+import com.facebook.drawee.view.SimpleDraweeView
+import com.facebook.imagepipeline.common.RotationOptions
+import com.facebook.imagepipeline.image.ImageInfo
+import com.facebook.imagepipeline.request.ImageRequestBuilder
+import com.nextcloud.talk.models.database.UserEntity
+import com.nextcloud.talk.shareditems.model.SharedItem
+import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.DrawableUtils
+import com.nextcloud.talk.utils.FileViewerUtils
+
+abstract class SharedItemsViewHolder(
+ open val binding: ViewBinding,
+ private val userEntity: UserEntity
+) : RecyclerView.ViewHolder(binding.root) {
+
+ companion object {
+ private val TAG = SharedItemsViewHolder::class.simpleName
+ }
+
+ abstract val image: SimpleDraweeView
+ abstract val clickTarget: View
+ abstract val progressBar: ProgressBar
+
+ private val authHeader = mapOf(
+ Pair(
+ "Authorization",
+ ApiUtils.getCredentials(userEntity.username, userEntity.token)
+ )
+ )
+
+ open fun onBind(item: SharedItem) {
+ image.hierarchy.setPlaceholderImage(staticImage(item.mimeType, image))
+ if (item.previewAvailable == true) {
+ image.controller = configurePreview(item)
+ }
+
+ /*
+ The FileViewerUtils forces us to do things at this points which should be done separated in the activity and
+ the view model.
+
+ This should be done after a refactoring of FileViewerUtils.
+ */
+ val fileViewerUtils = FileViewerUtils(image.context, userEntity)
+
+ clickTarget.setOnClickListener {
+ fileViewerUtils.openFile(
+ FileViewerUtils.FileInfo(item.id, item.name, item.fileSize),
+ item.path,
+ item.link,
+ item.mimeType,
+ FileViewerUtils.ProgressUi(
+ progressBar,
+ null,
+ image
+ )
+ )
+ }
+
+ fileViewerUtils.resumeToUpdateViewsByProgress(
+ item.name,
+ item.id,
+ item.mimeType,
+ FileViewerUtils.ProgressUi(progressBar, null, image)
+ )
+ }
+
+ private fun configurePreview(item: SharedItem): DraweeController {
+
+ val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(item.previewLink))
+ .setProgressiveRenderingEnabled(true)
+ .setRotationOptions(RotationOptions.autoRotate())
+ .disableDiskCache()
+ .setHeaders(authHeader)
+ .build()
+
+ val listener: ControllerListener = object : BaseControllerListener() {
+ override fun onFailure(id: String, e: Throwable) {
+ Log.w(TAG, "Failed to load image. A static mimetype image will be used", e)
+ }
+ }
+
+ return Fresco.newDraweeControllerBuilder()
+ .setOldController(image.controller)
+ .setAutoPlayAnimations(true)
+ .setImageRequest(imageRequest)
+ .setControllerListener(listener)
+ .build()
+ }
+
+ private fun staticImage(
+ mimeType: String?,
+ image: SimpleDraweeView
+ ): Drawable {
+ val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(mimeType)
+ return ContextCompat.getDrawable(image.context, drawableResourceId)!!
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedItem.kt b/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedItem.kt
new file mode 100644
index 000000000..3a7033fe4
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedItem.kt
@@ -0,0 +1,35 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.model
+
+data class SharedItem(
+ val id: String,
+ val name: String,
+ val fileSize: Long?,
+ val date: Long,
+ val path: String,
+ val link: String?,
+ val mimeType: String?,
+ val previewAvailable: Boolean?,
+ val previewLink: String
+)
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedItemType.kt b/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedItemType.kt
new file mode 100644
index 000000000..caedd4c02
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedItemType.kt
@@ -0,0 +1,40 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.model
+
+import java.util.Locale
+
+enum class SharedItemType {
+
+ AUDIO,
+ FILE,
+ MEDIA,
+ VOICE,
+ LOCATION,
+ DECKCARD,
+ OTHER;
+
+ companion object {
+ fun typeFor(name: String) = valueOf(name.uppercase(Locale.ROOT))
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedMediaItems.kt b/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedMediaItems.kt
new file mode 100644
index 000000000..ef891ffc7
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/model/SharedMediaItems.kt
@@ -0,0 +1,29 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.model
+
+class SharedMediaItems(
+ val items: List,
+ var lastSeenId: Int?,
+ var moreItemsExisting: Boolean
+)
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt
new file mode 100644
index 000000000..a668d672d
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt
@@ -0,0 +1,47 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.repositories
+
+import com.nextcloud.talk.shareditems.model.SharedItemType
+import com.nextcloud.talk.shareditems.model.SharedMediaItems
+import io.reactivex.Observable
+
+interface SharedItemsRepository {
+
+ fun media(parameters: Parameters, type: SharedItemType): Observable?
+
+ fun media(
+ parameters: Parameters,
+ type: SharedItemType,
+ lastKnownMessageId: Int?
+ ): Observable?
+
+ fun availableTypes(parameters: Parameters): Observable>
+
+ data class Parameters(
+ val userName: String,
+ val userToken: String,
+ val baseUrl: String,
+ val roomToken: String
+ )
+}
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt
new file mode 100644
index 000000000..22745b088
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt
@@ -0,0 +1,148 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.repositories
+
+import android.util.Log
+import com.nextcloud.talk.R
+import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
+import com.nextcloud.talk.models.json.chat.ChatShareOverall
+import com.nextcloud.talk.shareditems.model.SharedItem
+import com.nextcloud.talk.shareditems.model.SharedItemType
+import com.nextcloud.talk.shareditems.model.SharedMediaItems
+import com.nextcloud.talk.utils.ApiUtils
+import io.reactivex.Observable
+import retrofit2.Response
+import java.util.Locale
+import javax.inject.Inject
+
+class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi) : SharedItemsRepository {
+
+ override fun media(
+ parameters: SharedItemsRepository.Parameters,
+ type: SharedItemType
+ ): Observable? {
+ return media(parameters, type, null)
+ }
+
+ override fun media(
+ parameters: SharedItemsRepository.Parameters,
+ type: SharedItemType,
+ lastKnownMessageId: Int?
+ ): Observable? {
+ val credentials = ApiUtils.getCredentials(parameters.userName, parameters.userToken)
+
+ return ncApi.getSharedItems(
+ credentials,
+ ApiUtils.getUrlForChatSharedItems(1, parameters.baseUrl, parameters.roomToken),
+ type.toString().lowercase(Locale.ROOT),
+ lastKnownMessageId,
+ BATCH_SIZE
+ ).map { map(it, parameters) }
+ }
+
+ private fun map(
+ response: Response,
+ parameters: SharedItemsRepository.Parameters
+ ): SharedMediaItems {
+
+ var chatLastGiven: Int? = null
+ val items = mutableMapOf()
+
+ if (response.headers()["x-chat-last-given"] != null) {
+ chatLastGiven = response.headers()["x-chat-last-given"]!!.toInt()
+ }
+
+ val mediaItems = response.body()!!.ocs!!.data
+ if (mediaItems != null) {
+ for (it in mediaItems) {
+ if (it.value.messageParameters?.containsKey("file") == true) {
+ 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"]!!.toLong(),
+ it.value.timestamp,
+ fileParameters["path"]!!,
+ fileParameters["link"]!!,
+ fileParameters["mimetype"]!!,
+ previewAvailable,
+ previewLink(fileParameters["id"], parameters.baseUrl)
+ )
+ } else {
+ Log.w(TAG, "location and deckcard are not yet supported")
+ }
+ }
+ }
+
+ val sortedMutableItems = items.toSortedMap().values.toList().reversed().toMutableList()
+ val moreItemsExisting = items.count() == BATCH_SIZE
+
+ return SharedMediaItems(
+ sortedMutableItems,
+ chatLastGiven,
+ moreItemsExisting
+ )
+ }
+
+ override fun availableTypes(parameters: SharedItemsRepository.Parameters): Observable> {
+ val credentials = ApiUtils.getCredentials(parameters.userName, parameters.userToken)
+
+ return ncApi.getSharedItemsOverview(
+ credentials,
+ ApiUtils.getUrlForChatSharedItemsOverview(1, parameters.baseUrl, parameters.roomToken),
+ 1
+ ).map {
+ val types = mutableSetOf()
+ val typeMap = it.body()!!.ocs!!.data!!
+ for (t in typeMap) {
+ if (t.value.isNotEmpty()) {
+ try {
+ types += SharedItemType.typeFor(t.key)
+ } catch (e: IllegalArgumentException) {
+ Log.w(TAG, "Server responds an unknown shared item type: ${t.key}")
+ }
+ }
+ }
+
+ types.toSet()
+ }
+ }
+
+ private fun previewLink(fileId: String?, baseUrl: String): String {
+ return ApiUtils.getUrlForFilePreviewWithFileId(
+ baseUrl,
+ fileId,
+ sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size)
+ )
+ }
+
+ companion object {
+ const val BATCH_SIZE: Int = 28
+ private val TAG = SharedItemsRepositoryImpl::class.simpleName
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/shareditems/viewmodels/SharedItemsViewModel.kt
new file mode 100644
index 000000000..e26b5cbe6
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/shareditems/viewmodels/SharedItemsViewModel.kt
@@ -0,0 +1,179 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Tim Krüger
+ * @author Álvaro Brey
+ * Copyright (C) 2022 Álvaro Brey
+ * Copyright (C) 2022 Tim Krüger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.shareditems.viewmodels
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.nextcloud.talk.models.database.UserEntity
+import com.nextcloud.talk.shareditems.model.SharedItemType
+import com.nextcloud.talk.shareditems.model.SharedMediaItems
+import com.nextcloud.talk.shareditems.repositories.SharedItemsRepository
+import io.reactivex.Observer
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import javax.inject.Inject
+
+class SharedItemsViewModel @Inject constructor(
+ private val repository: SharedItemsRepository
+) :
+ ViewModel() {
+
+ private lateinit var repositoryParameters: SharedItemsRepository.Parameters
+
+ sealed interface ViewState
+ object InitialState : ViewState
+ object NoSharedItemsState : ViewState
+ open class TypesLoadedState(val types: Set, val selectedType: SharedItemType) : ViewState
+ class LoadingItemsState(types: Set, selectedType: SharedItemType) :
+ TypesLoadedState(types, selectedType)
+
+ class LoadedState(types: Set, selectedType: SharedItemType, val items: SharedMediaItems) :
+ TypesLoadedState(types, selectedType)
+
+ private val _viewState: MutableLiveData = MutableLiveData(InitialState)
+ val viewState: LiveData
+ get() = _viewState
+
+ fun initialize(userEntity: UserEntity, roomToken: String) {
+ repositoryParameters = SharedItemsRepository.Parameters(
+ userEntity.userId,
+ userEntity.token,
+ userEntity.baseUrl,
+ roomToken
+ )
+ loadAvailableTypes()
+ }
+
+ private fun loadAvailableTypes() {
+ repository.availableTypes(repositoryParameters).subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(object : Observer> {
+
+ var types: Set? = null
+
+ override fun onSubscribe(d: Disposable) = Unit
+
+ override fun onNext(types: Set) {
+ this.types = types
+ }
+
+ override fun onError(e: Throwable) {
+ Log.d(TAG, "An error occurred: $e")
+ }
+
+ override fun onComplete() {
+ val newTypes = this.types
+ if (newTypes.isNullOrEmpty()) {
+ this@SharedItemsViewModel._viewState.value = NoSharedItemsState
+ } else {
+ val selectedType = chooseInitialType(newTypes)
+ this@SharedItemsViewModel._viewState.value =
+ TypesLoadedState(newTypes, selectedType)
+ initialLoadItems(selectedType)
+ }
+ }
+ })
+ }
+
+ private fun chooseInitialType(newTypes: Set): SharedItemType = when {
+ newTypes.contains(SharedItemType.MEDIA) -> SharedItemType.MEDIA
+ else -> newTypes.toList().first()
+ }
+
+ fun initialLoadItems(type: SharedItemType) {
+ val state = _viewState.value
+ if (state is TypesLoadedState) {
+ _viewState.value = LoadingItemsState(state.types, type)
+ repository.media(repositoryParameters, type)?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(SharedMediaItemsObserver())
+ }
+ }
+
+ fun loadNextItems() {
+ when (val currentState = _viewState.value) {
+ is LoadedState -> {
+ val currentSharedItems = currentState.items
+ if (currentSharedItems.moreItemsExisting) {
+ repository.media(repositoryParameters, currentState.selectedType, currentSharedItems.lastSeenId)
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(SharedMediaItemsObserver())
+ }
+ }
+ else -> return
+ }
+ }
+
+ inner class SharedMediaItemsObserver : Observer {
+
+ var newSharedItems: SharedMediaItems? = null
+
+ override fun onSubscribe(d: Disposable) = Unit
+
+ override fun onNext(response: SharedMediaItems) {
+ newSharedItems = response
+ }
+
+ override fun onError(e: Throwable) {
+ Log.d(TAG, "An error occurred: $e")
+ }
+
+ override fun onComplete() {
+ val items = newSharedItems!!
+ val state = this@SharedItemsViewModel._viewState.value
+ if (state is LoadedState) {
+ val oldItems = state.items.items
+ val newItems =
+ SharedMediaItems(
+ oldItems + newSharedItems!!.items,
+ newSharedItems!!.lastSeenId,
+ newSharedItems!!.moreItemsExisting
+ )
+ setCurrentState(newItems)
+ } else {
+ setCurrentState(items)
+ }
+ }
+
+ private fun setCurrentState(items: SharedMediaItems) {
+ when (val state = this@SharedItemsViewModel._viewState.value) {
+ is TypesLoadedState -> {
+ this@SharedItemsViewModel._viewState.value = LoadedState(
+ state.types,
+ state.selectedType,
+ items
+ )
+ }
+ else -> return
+ }
+ }
+ }
+
+ companion object {
+ private val TAG = SharedItemsViewModel::class.simpleName
+ }
+}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt
index f3eaf5b34..03c7fca0f 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt
@@ -52,6 +52,12 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_ID
import java.io.File
import java.util.concurrent.ExecutionException
+/*
+Usage of this class forces us to do things at one location which should be separated in a activity and view model.
+
+Example:
+ - SharedItemsViewHolder
+ */
class FileViewerUtils(private val context: Context, private val userEntity: UserEntity) {
fun openFile(
diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt
deleted file mode 100644
index 03210d6cf..000000000
--- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt
+++ /dev/null
@@ -1,192 +0,0 @@
-package com.nextcloud.talk.viewmodels
-
-import android.util.Log
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import com.nextcloud.talk.models.database.UserEntity
-import com.nextcloud.talk.models.json.chat.ChatShareOverall
-import com.nextcloud.talk.models.json.chat.ChatShareOverviewOverall
-import com.nextcloud.talk.repositories.SharedItem
-import com.nextcloud.talk.repositories.SharedItemType
-import com.nextcloud.talk.repositories.SharedItemsRepository
-import com.nextcloud.talk.repositories.SharedMediaItems
-import io.reactivex.Observer
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.disposables.Disposable
-import io.reactivex.schedulers.Schedulers
-import retrofit2.Response
-
-class SharedItemsViewModel(private val repository: SharedItemsRepository, private val initialType: SharedItemType) :
- ViewModel() {
-
- private val _sharedItemType: MutableLiveData> by lazy {
- MutableLiveData>().also {
- availableTypes()
- }
- }
-
- private val _sharedItems: MutableLiveData by lazy {
- MutableLiveData().also {
- loadItems(initialType)
- }
- }
-
- val sharedItemType: LiveData>
- get() = _sharedItemType
-
- val sharedItems: LiveData
- get() = _sharedItems
-
- 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: SharedItemType) {
- repository.media(type)?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.subscribe(observer(type, true))
- }
-
- private fun observer(type: SharedItemType, initModel: Boolean): Observer> {
- return object : Observer> {
-
- var chatLastGiven: Int? = null
- val items = mutableMapOf()
-
- override fun onSubscribe(d: Disposable) = Unit
-
- override fun onNext(response: Response) {
-
- if (response.headers()["x-chat-last-given"] != null) {
- chatLastGiven = response.headers()["x-chat-last-given"]!!.toInt()
- }
-
- val mediaItems = response.body()!!.ocs!!.data
- if (mediaItems != null) {
- for (it in mediaItems) {
- 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"]?.toLong(),
- it.value.timestamp,
- 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() == BATCH_SIZE
-
- if (initModel) {
- this@SharedItemsViewModel._sharedItems.value =
- SharedMediaItems(
- 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,
- chatLastGiven,
- moreItemsExisting,
- repository.authHeader()
- )
- }
- }
- }
- }
-
- private fun availableTypes() {
- repository.availableTypes()?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.subscribe(object : Observer> {
-
- val types = mutableSetOf()
-
- override fun onSubscribe(d: Disposable) = Unit
-
- override fun onNext(response: Response) {
- val typeMap = response.body()!!.ocs!!.data!!
- for (it in typeMap) {
- if (it.value.size > 0) {
- try {
- types += SharedItemType.typeFor(it.key)
- } catch (e: IllegalArgumentException) {
- Log.w(TAG, "Server responds an unknown shared item type: ${it.key}")
- }
- }
- }
- }
-
- override fun onError(e: Throwable) {
- Log.d(TAG, "An error occurred: $e")
- }
-
- override fun onComplete() {
- this@SharedItemsViewModel._sharedItemType.value = types
- }
- })
- }
-
- class Factory(val userEntity: UserEntity, val roomToken: String, private val initialType: SharedItemType) :
- ViewModelProvider
- .Factory {
-
- override fun create(modelClass: Class): T {
- if (modelClass.isAssignableFrom(SharedItemsViewModel::class.java)) {
-
- val repository = SharedItemsRepository()
- repository.parameters = SharedItemsRepository.Parameters(
- userEntity.userId,
- userEntity.token,
- userEntity.baseUrl,
- userEntity,
- roomToken
- )
-
- return SharedItemsViewModel(repository, initialType) as T
- }
-
- throw IllegalArgumentException("Unknown ViewModel class")
- }
- }
-
- companion object {
- private val TAG = SharedItemsViewModel::class.simpleName
- const val BATCH_SIZE: Int = 28
- }
-}
diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml
index 5b2b2f6b5..54243b293 100644
--- a/app/src/main/res/layout/activity_shared_items.xml
+++ b/app/src/main/res/layout/activity_shared_items.xml
@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_default"
- tools:context=".activities.SharedItemsActivity">
+ tools:context=".shareditems.activities.SharedItemsActivity">
+
+
Shared items
Images, files, voice messages…
+ No shared items
Talk recording from %1$s (%2$s)