Skip to content

Commit e02b01f

Browse files
Merge pull request #16986 from nextcloud/fix/gallery-fragment-oome
fix(gallery-fragment): oome
2 parents 7a80672 + 2821fe9 commit e02b01f

5 files changed

Lines changed: 89 additions & 85 deletions

File tree

app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ interface FileDao {
5656
)
5757
fun getGalleryItems(startDate: Long, endDate: Long, fileOwner: String): List<FileEntity>
5858

59+
@Query(
60+
"SELECT * FROM filelist WHERE modified >= :startDate" +
61+
" AND modified < :endDate" +
62+
" AND (content_type LIKE 'image/%' OR content_type LIKE 'video/%')" +
63+
" AND file_owner = :fileOwner" +
64+
" ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER}"
65+
)
66+
suspend fun getGalleryItemsSuspended(startDate: Long, endDate: Long, fileOwner: String): List<FileEntity>
67+
5968
@Query("SELECT * FROM filelist WHERE file_owner = :fileOwner ORDER BY ${ProviderTableMeta.FILE_DEFAULT_SORT_ORDER}")
6069
fun getAllFiles(fileOwner: String): List<FileEntity>
6170

app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ suspend fun FileDataStorageManager.saveShares(shares: List<OCShare>, accountName
2525
}
2626
}
2727

28+
suspend fun FileDataStorageManager.getAllGalleryItemsSuspended(): List<OCFile> {
29+
val fileEntities = fileDao.getGalleryItemsSuspended(0, Long.MAX_VALUE, user.accountName)
30+
return fileEntities.map { createFileInstance(it) }
31+
}
32+
2833
fun FileDataStorageManager.searchFilesByName(file: OCFile, accountName: String, query: String): List<OCFile> =
2934
fileDao.searchFilesInFolder(file.fileId, accountName, query).map {
3035
createFileInstance(it)

app/src/main/java/com/nextcloud/utils/extensions/OCFileExtensions.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
package com.nextcloud.utils.extensions
99

1010
import com.owncloud.android.MainApp
11+
import com.owncloud.android.datamodel.GalleryItems
12+
import com.owncloud.android.datamodel.GalleryRow
1113
import com.owncloud.android.datamodel.OCFile
1214
import com.owncloud.android.datamodel.OCFileDepth
1315
import com.owncloud.android.datamodel.OCFileDepth.DeepLevel
1416
import com.owncloud.android.datamodel.OCFileDepth.FirstLevel
1517
import com.owncloud.android.datamodel.OCFileDepth.Root
1618
import com.owncloud.android.utils.FileStorageUtils
19+
import java.util.Calendar
20+
import java.util.Date
1721

1822
fun List<OCFile>.filterFilenames(): List<OCFile> = distinctBy { it.fileName }
1923

@@ -50,3 +54,31 @@ fun OCFile?.getDepth(): OCFileDepth? {
5054
// Otherwise, it's a subdirectory of a subdirectory
5155
return DeepLevel
5256
}
57+
58+
fun List<OCFile>.toGalleryItems(columns: Int, defaultSize: Int): List<GalleryItems> {
59+
if (isEmpty()) return emptyList()
60+
61+
val calendar = Calendar.getInstance()
62+
return groupBy {
63+
calendar.timeInMillis = it.modificationTimestamp
64+
calendar.set(Calendar.DAY_OF_MONTH, 1)
65+
calendar.set(Calendar.HOUR_OF_DAY, 0)
66+
calendar.set(Calendar.MINUTE, 0)
67+
calendar.set(Calendar.SECOND, 0)
68+
calendar.set(Calendar.MILLISECOND, 0)
69+
calendar.timeInMillis
70+
}
71+
.map { (date, filesList) ->
72+
GalleryItems(date, transformToRows(filesList, columns, defaultSize))
73+
}
74+
.sortedByDescending { it.date }
75+
}
76+
77+
private fun transformToRows(list: List<OCFile>, columns: Int, defaultSize: Int): List<GalleryRow> {
78+
if (list.isEmpty()) return emptyList()
79+
80+
return list
81+
.sortedByDescending { it.modificationTimestamp }
82+
.chunked(columns)
83+
.map { chunk -> GalleryRow(chunk, defaultSize, defaultSize) }
84+
}

app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt

Lines changed: 4 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,19 @@ import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter
2323
import com.afollestad.sectionedrecyclerview.SectionedViewHolder
2424
import com.nextcloud.client.account.User
2525
import com.nextcloud.client.preferences.AppPreferences
26+
import com.nextcloud.utils.extensions.toGalleryItems
2627
import com.owncloud.android.databinding.GalleryHeaderBinding
2728
import com.owncloud.android.databinding.GalleryRowBinding
2829
import com.owncloud.android.datamodel.FileDataStorageManager
2930
import com.owncloud.android.datamodel.GalleryItems
30-
import com.owncloud.android.datamodel.GalleryRow
3131
import com.owncloud.android.datamodel.OCFile
3232
import com.owncloud.android.lib.common.utils.Log_OC
3333
import com.owncloud.android.ui.activity.ComponentsGetter
34-
import com.owncloud.android.ui.fragment.GalleryFragment
35-
import com.owncloud.android.ui.fragment.GalleryFragmentBottomSheetDialog
36-
import com.owncloud.android.ui.fragment.SearchType
3734
import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface
3835
import com.owncloud.android.utils.DisplayUtils
3936
import com.owncloud.android.utils.FileSortOrder
40-
import com.owncloud.android.utils.MimeTypeUtil
4137
import com.owncloud.android.utils.theme.ViewThemeUtils
4238
import me.zhanghai.android.fastscroll.PopupTextProvider
43-
import java.util.Calendar
44-
import java.util.Date
4539

4640
@Suppress("LongParameterList", "TooManyFunctions")
4741
class GalleryAdapter(
@@ -210,72 +204,17 @@ class GalleryAdapter(
210204
}
211205

212206
@SuppressLint("NotifyDataSetChanged")
213-
fun showAllGalleryItems(
214-
remotePath: String,
215-
mediaState: GalleryFragmentBottomSheetDialog.MediaState,
216-
photoFragment: GalleryFragment
217-
) {
218-
val items = storageManager.allGalleryItems
219-
220-
val filteredList = items.filter { it != null && it.remotePath.startsWith(remotePath) }
221-
222-
setMediaFilter(
223-
filteredList,
224-
mediaState,
225-
photoFragment
226-
)
227-
}
228-
229-
// Set Image/Video List According to Selection of Hide/Show Image/Video
230-
@SuppressLint("NotifyDataSetChanged")
231-
private fun setMediaFilter(
232-
items: List<OCFile>,
233-
mediaState: GalleryFragmentBottomSheetDialog.MediaState,
234-
photoFragment: GalleryFragment
235-
) {
236-
val finalSortedList: List<OCFile> = when (mediaState) {
237-
GalleryFragmentBottomSheetDialog.MediaState.MEDIA_STATE_PHOTOS_ONLY -> {
238-
items.filter { MimeTypeUtil.isImage(it.mimeType) }.distinct()
239-
}
240-
241-
GalleryFragmentBottomSheetDialog.MediaState.MEDIA_STATE_VIDEOS_ONLY -> {
242-
items.filter { MimeTypeUtil.isVideo(it.mimeType) }.distinct()
243-
}
244-
245-
else -> items
246-
}
247-
248-
if (finalSortedList.isEmpty()) {
249-
photoFragment.setEmptyListMessage(SearchType.GALLERY_SEARCH)
250-
}
251-
252-
files = finalSortedList.toGalleryItems()
207+
fun updateList(items: List<GalleryItems>) {
208+
files = items
253209
notifyDataSetChanged()
254210
}
255211

256-
private fun transformToRows(list: List<OCFile>): List<GalleryRow> {
257-
if (list.isEmpty()) return emptyList()
258-
259-
return list
260-
.sortedByDescending { it.modificationTimestamp }
261-
.chunked(columns)
262-
.map { chunk -> GalleryRow(chunk, defaultThumbnailSize, defaultThumbnailSize) }
263-
}
264-
265212
@SuppressLint("NotifyDataSetChanged")
266213
fun clear() {
267214
files = emptyList()
268215
notifyDataSetChanged()
269216
}
270217

271-
private fun firstOfMonth(timestamp: Long): Long = Calendar.getInstance().apply {
272-
time = Date(timestamp)
273-
set(Calendar.DAY_OF_MONTH, getActualMinimum(Calendar.DAY_OF_MONTH))
274-
set(Calendar.HOUR_OF_DAY, 0)
275-
set(Calendar.MINUTE, 0)
276-
set(Calendar.SECOND, 0)
277-
}.timeInMillis
278-
279218
fun isEmpty(): Boolean = files.isEmpty()
280219

281220
fun getItem(position: Int): OCFile? {
@@ -365,21 +304,11 @@ class GalleryAdapter(
365304
val allFiles = getAllFiles()
366305
allFiles.firstOrNull { it.remotePath == remotePath }?.also { file ->
367306
file.isFavorite = favorite
368-
files = allFiles.toGalleryItems()
307+
files = allFiles.toGalleryItems(columns, defaultThumbnailSize)
369308
notifyItemChanged(file)
370309
}
371310
}
372311

373-
private fun List<OCFile>.toGalleryItems(): List<GalleryItems> {
374-
if (isEmpty()) return emptyList()
375-
376-
return groupBy { firstOfMonth(it.modificationTimestamp) }
377-
.map { (date, filesList) ->
378-
GalleryItems(date, transformToRows(filesList))
379-
}
380-
.sortedByDescending { it.date }
381-
}
382-
383312
override fun onBindFooterViewHolder(holder: SectionedViewHolder?, section: Int) = Unit
384313

385314
override fun swapDirectory(

app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.kt

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ import androidx.activity.result.contract.ActivityResultContracts
2424
import androidx.core.view.MenuHost
2525
import androidx.core.view.MenuProvider
2626
import androidx.lifecycle.Lifecycle
27+
import androidx.lifecycle.lifecycleScope
2728
import androidx.localbroadcastmanager.content.LocalBroadcastManager
2829
import androidx.recyclerview.widget.GridLayoutManager
2930
import androidx.recyclerview.widget.RecyclerView
31+
import com.nextcloud.utils.extensions.getAllGalleryItemsSuspended
3032
import com.nextcloud.utils.extensions.getParcelableArgument
3133
import kotlinx.coroutines.Job
3234
import com.nextcloud.utils.extensions.getTypedActivity
35+
import com.nextcloud.utils.extensions.toGalleryItems
3336
import com.owncloud.android.BuildConfig
3437
import com.owncloud.android.R
3538
import com.owncloud.android.datamodel.OCFile
@@ -44,13 +47,18 @@ import com.owncloud.android.ui.adapter.GalleryAdapter
4447
import com.owncloud.android.ui.asynctasks.GallerySearchTask
4548
import com.owncloud.android.ui.events.ChangeMenuEvent
4649
import com.owncloud.android.ui.fragment.GalleryFragmentBottomSheetDialog.MediaState
50+
import com.owncloud.android.utils.MimeTypeUtil
51+
import kotlinx.coroutines.Dispatchers
52+
import kotlinx.coroutines.launch
53+
import kotlinx.coroutines.withContext
4754

4855
@Suppress("ForbiddenComment", "ReturnCount", "MagicNumber", "MaxLineLength")
4956
class GalleryFragment :
5057
OCFileListFragment(),
5158
GalleryFragmentBottomSheetActions {
5259
var isPhotoSearchQueryRunning: Boolean = false
5360
private var photoSearchTask: Job? = null
61+
private var showGalleryJob: Job? = null
5462
private var endDate: Long = 0
5563
private val limit = 150
5664
private var adapter: GalleryAdapter? = null
@@ -124,10 +132,11 @@ class GalleryFragment :
124132
}
125133

126134
override fun onDestroyView() {
127-
if (photoSearchTask != null) {
128-
photoSearchTask?.cancel()
129-
photoSearchTask = null
130-
}
135+
showGalleryJob?.cancel()
136+
showGalleryJob = null
137+
138+
photoSearchTask?.cancel()
139+
photoSearchTask = null
131140

132141
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(refreshSearchEventReceiver)
133142

@@ -372,12 +381,32 @@ class GalleryFragment :
372381
fun showAllGalleryItems() {
373382
val mediaState = bottomSheet?.currMediaState ?: return
374383

375-
adapter?.showAllGalleryItems(
376-
preferences.getLastSelectedMediaFolder(),
377-
mediaState,
378-
this
379-
)
380-
updateSubtitle(mediaState)
384+
showGalleryJob?.cancel()
385+
showGalleryJob = lifecycleScope.launch(Dispatchers.Default) {
386+
val remotePath = preferences.getLastSelectedMediaFolder()
387+
val items = mContainerActivity.storageManager.getAllGalleryItemsSuspended()
388+
389+
val isPhotosOnly = mediaState == MediaState.MEDIA_STATE_PHOTOS_ONLY
390+
val isVideosOnly = mediaState == MediaState.MEDIA_STATE_VIDEOS_ONLY
391+
392+
val filteredItems = items.filter {
393+
if (!it.remotePath.startsWith(remotePath)) return@filter false
394+
if (isPhotosOnly) return@filter MimeTypeUtil.isImage(it.mimeType)
395+
if (isVideosOnly) return@filter MimeTypeUtil.isVideo(it.mimeType)
396+
true
397+
}
398+
399+
val galleryItems =
400+
filteredItems.toGalleryItems(columnsCount, ThumbnailsCacheManager.getThumbnailDimension())
401+
402+
withContext(Dispatchers.Main) {
403+
if (galleryItems.isEmpty()) {
404+
setEmptyListMessage(SearchType.GALLERY_SEARCH)
405+
}
406+
adapter?.updateList(galleryItems)
407+
updateSubtitle(mediaState)
408+
}
409+
}
381410
}
382411

383412
private fun updateSubtitle(mediaState: MediaState?) {

0 commit comments

Comments
 (0)