FileActionsViewModel.kt 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Álvaro Brey
  5. * Copyright (C) 2022 Álvaro Brey
  6. * Copyright (C) 2022 Nextcloud GmbH
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. package com.nextcloud.ui.fileactions
  23. import android.os.Bundle
  24. import androidx.annotation.IdRes
  25. import androidx.lifecycle.LiveData
  26. import androidx.lifecycle.MutableLiveData
  27. import androidx.lifecycle.ViewModel
  28. import androidx.lifecycle.viewModelScope
  29. import com.nextcloud.client.account.CurrentAccountProvider
  30. import com.nextcloud.client.logger.Logger
  31. import com.nextcloud.utils.TimeConstants
  32. import com.owncloud.android.datamodel.OCFile
  33. import com.owncloud.android.files.FileMenuFilter
  34. import com.owncloud.android.lib.resources.files.model.FileLockType
  35. import com.owncloud.android.ui.activity.ComponentsGetter
  36. import kotlinx.coroutines.Dispatchers
  37. import kotlinx.coroutines.launch
  38. import javax.inject.Inject
  39. class FileActionsViewModel @Inject constructor(
  40. private val currentAccountProvider: CurrentAccountProvider,
  41. private val filterFactory: FileMenuFilter.Factory,
  42. private val logger: Logger
  43. ) :
  44. ViewModel() {
  45. data class LockInfo(val lockType: FileLockType, val lockedBy: String, val lockedUntil: Long?)
  46. sealed interface UiState {
  47. object Loading : UiState
  48. object Error : UiState
  49. data class LoadedForSingleFile(
  50. val actions: List<FileAction>,
  51. val titleFile: OCFile?,
  52. val lockInfo: LockInfo? = null
  53. ) : UiState
  54. data class LoadedForMultipleFiles(val actions: List<FileAction>, val fileCount: Int) : UiState
  55. }
  56. private val _uiState: MutableLiveData<UiState> = MutableLiveData(UiState.Loading)
  57. val uiState: LiveData<UiState>
  58. get() = _uiState
  59. private val _clickActionId: MutableLiveData<Int?> = MutableLiveData(null)
  60. val clickActionId: LiveData<Int?>
  61. @IdRes
  62. get() = _clickActionId
  63. fun load(
  64. arguments: Bundle,
  65. componentsGetter: ComponentsGetter
  66. ) {
  67. val files: List<OCFile>? = arguments.getParcelableArrayList(ARG_FILES)
  68. val numberOfAllFiles: Int = arguments.getInt(ARG_ALL_FILES_COUNT, 1)
  69. val isOverflow = arguments.getBoolean(ARG_IS_OVERFLOW, false)
  70. val additionalFilter: IntArray? = arguments.getIntArray(ARG_ADDITIONAL_FILTER)
  71. val inSingleFileFragment = arguments.getBoolean(ARG_IN_SINGLE_FILE_FRAGMENT)
  72. if (files.isNullOrEmpty()) {
  73. logger.d(TAG, "No valid files argument for loading actions")
  74. _uiState.postValue(UiState.Error)
  75. } else {
  76. load(componentsGetter, files.toList(), numberOfAllFiles, isOverflow, additionalFilter, inSingleFileFragment)
  77. }
  78. }
  79. private fun load(
  80. componentsGetter: ComponentsGetter,
  81. files: Collection<OCFile>,
  82. numberOfAllFiles: Int?,
  83. isOverflow: Boolean?,
  84. additionalFilter: IntArray?,
  85. isSingleFileFragment: Boolean = false
  86. ) {
  87. viewModelScope.launch(Dispatchers.IO) {
  88. val toHide = getHiddenActions(componentsGetter, numberOfAllFiles, files, isOverflow, isSingleFileFragment)
  89. val availableActions = getActionsToShow(additionalFilter, toHide)
  90. updateStateLoaded(files, availableActions)
  91. }
  92. }
  93. private fun getHiddenActions(
  94. componentsGetter: ComponentsGetter,
  95. numberOfAllFiles: Int?,
  96. files: Collection<OCFile>,
  97. isOverflow: Boolean?,
  98. inSingleFileFragment: Boolean
  99. ): List<Int> {
  100. return filterFactory.newInstance(
  101. numberOfAllFiles ?: 1,
  102. files.toList(),
  103. componentsGetter,
  104. isOverflow ?: false,
  105. currentAccountProvider.user
  106. )
  107. .getToHide(inSingleFileFragment)
  108. }
  109. private fun getActionsToShow(
  110. additionalFilter: IntArray?,
  111. toHide: List<Int>
  112. ) = FileAction.SORTED_VALUES
  113. .filter { additionalFilter == null || it.id !in additionalFilter }
  114. .filter { it.id !in toHide }
  115. private fun updateStateLoaded(
  116. files: Collection<OCFile>,
  117. availableActions: List<FileAction>
  118. ) {
  119. val state: UiState = when (files.size) {
  120. 1 -> {
  121. val file = files.first()
  122. UiState.LoadedForSingleFile(availableActions, file, getLockInfo(file))
  123. }
  124. else -> UiState.LoadedForMultipleFiles(availableActions, files.size)
  125. }
  126. _uiState.postValue(state)
  127. }
  128. private fun getLockInfo(file: OCFile): LockInfo? {
  129. val lockType = file.lockType
  130. val username = file.lockOwnerDisplayName ?: file.lockOwnerId
  131. return if (file.isLocked && lockType != null && username != null) {
  132. LockInfo(lockType, username, getLockedUntil(file))
  133. } else {
  134. null
  135. }
  136. }
  137. private fun getLockedUntil(file: OCFile): Long? {
  138. return if (file.lockTimestamp == 0L || file.lockTimeout == 0L) {
  139. null
  140. } else {
  141. (file.lockTimestamp + file.lockTimeout) * TimeConstants.MILLIS_PER_SECOND
  142. }
  143. }
  144. fun onClick(action: FileAction) {
  145. _clickActionId.value = action.id
  146. }
  147. companion object {
  148. const val ARG_ALL_FILES_COUNT = "ALL_FILES_COUNT"
  149. const val ARG_FILES = "FILES"
  150. const val ARG_IS_OVERFLOW = "OVERFLOW"
  151. const val ARG_ADDITIONAL_FILTER = "ADDITIONAL_FILTER"
  152. const val ARG_IN_SINGLE_FILE_FRAGMENT = "IN_SINGLE_FILE_FRAGMENT"
  153. private val TAG = FileActionsViewModel::class.simpleName!!
  154. }
  155. }