diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt index f570be0e8..1cac18206 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt @@ -283,8 +283,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { Thread { val filtered = mMedia.filter { it.name.contains(text, true) } as ArrayList filtered.sortBy { !it.name.startsWith(text, true) } + val groupedMedia = MediaFetcher(applicationContext).groupMedia(filtered, mPath) runOnUiThread { - getMediaAdapter()?.updateMedia(filtered) + getMediaAdapter()?.updateMedia(groupedMedia) } }.start() } @@ -315,12 +316,12 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { return } - val media = mMedia.clone() as ArrayList + val groupedMedia = MediaFetcher(applicationContext).groupMedia(mMedia.clone() as ArrayList, mPath) val currAdapter = media_grid.adapter if (currAdapter == null) { initZoomListener() val fastscroller = if (config.scrollHorizontally) media_horizontal_fastscroller else media_vertical_fastscroller - MediaAdapter(this, media, this, mIsGetImageIntent || mIsGetVideoIntent || mIsGetAnyIntent, mAllowPickingMultiple, media_grid, fastscroller, mPath) { + MediaAdapter(this, groupedMedia, this, mIsGetImageIntent || mIsGetVideoIntent || mIsGetAnyIntent, mAllowPickingMultiple, media_grid, fastscroller) { itemClicked((it as ThumbnailMedium).path) }.apply { setupZoomListener(mZoomListener) @@ -328,8 +329,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { } setupLayoutManager() } else { - (currAdapter as MediaAdapter).updateMedia(media) + (currAdapter as MediaAdapter).updateMedia(groupedMedia) } + setupScrollDirection() } @@ -359,8 +361,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { private fun getBubbleTextItem(index: Int, sorting: Int) = getMediaAdapter()?.getItemBubbleText(index, sorting) ?: "" private fun checkLastMediaChanged() { - if (isActivityDestroyed()) + if (isActivityDestroyed()) { return + } mLastMediaHandler.removeCallbacksAndMessages(null) mLastMediaHandler.postDelayed({ diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/MediaAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/MediaAdapter.kt index 3273a7d7f..6055e3893 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/MediaAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/MediaAdapter.kt @@ -2,7 +2,6 @@ package com.simplemobiletools.gallery.adapters import android.os.Handler import android.os.Looper -import android.text.format.DateFormat import android.view.Menu import android.view.View import android.view.ViewGroup @@ -19,19 +18,16 @@ import com.simplemobiletools.commons.views.MyRecyclerView import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.dialogs.DeleteWithRememberDialog import com.simplemobiletools.gallery.extensions.* -import com.simplemobiletools.gallery.helpers.* -import com.simplemobiletools.gallery.models.Medium +import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST import com.simplemobiletools.gallery.models.ThumbnailItem import com.simplemobiletools.gallery.models.ThumbnailMedium import com.simplemobiletools.gallery.models.ThumbnailSection import kotlinx.android.synthetic.main.photo_video_item_grid.view.* import kotlinx.android.synthetic.main.thumbnail_section.view.* -import java.util.* -import kotlin.collections.ArrayList -class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, val listener: MediaOperationsListener?, val isAGetIntent: Boolean, - val allowMultiplePicks: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, val path: String, - itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) { +class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, val listener: MediaOperationsListener?, val isAGetIntent: Boolean, + val allowMultiplePicks: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) : + MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) { private val INSTANT_LOAD_DURATION = 2000L private val IMAGE_LOAD_DELAY = 100L @@ -41,13 +37,10 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, private val config = activity.config private val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST private var visibleItemPaths = ArrayList() - private var thumbnailItems = ArrayList() private var loadImageInstantly = false private var delayHandler = Handler(Looper.getMainLooper()) private var currentMediaHash = media.hashCode() - private var currentGrouping = GROUP_BY_NONE private val hasOTGConnected = activity.hasOTGConnected() - private var mediumGroups = LinkedHashMap>() private var scrollHorizontally = config.scrollHorizontally private var animateGifs = config.animateGifs @@ -56,7 +49,6 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, init { setupDragListener(true) - groupMedia() enableInstantLoad() } @@ -84,7 +76,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, } override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) { - val tmbItem = thumbnailItems.getOrNull(position) ?: return + val tmbItem = media.getOrNull(position) ?: return if (tmbItem is ThumbnailMedium) { visibleItemPaths.add(tmbItem.path) } @@ -99,10 +91,10 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, bindViewHolder(holder, position, view) } - override fun getItemCount() = thumbnailItems.size + override fun getItemCount() = media.size override fun getItemViewType(position: Int): Int { - val tmbItem = thumbnailItems[position] + val tmbItem = media[position] return if (tmbItem is ThumbnailSection) { ITEM_SECTION } else { @@ -145,7 +137,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, } } - override fun getSelectableItemCount() = thumbnailItems.filter { it is ThumbnailMedium }.size + override fun getSelectableItemCount() = media.filter { it is ThumbnailMedium }.size override fun getIsItemSelectable(position: Int) = !isASectionTitle(position) @@ -161,7 +153,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, } } - fun isASectionTitle(position: Int) = thumbnailItems.getOrNull(position) is ThumbnailSection + fun isASectionTitle(position: Int) = media.getOrNull(position) is ThumbnailSection private fun checkHideBtnVisibility(menu: Menu) { var hiddenCnt = 0 @@ -199,7 +191,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, private fun showProperties() { if (selectedPositions.size <= 1) { - PropertiesDialog(activity, (thumbnailItems[selectedPositions.first()] as ThumbnailMedium).path, config.shouldShowHidden) + PropertiesDialog(activity, (media[selectedPositions.first()] as ThumbnailMedium).path, config.shouldShowHidden) } else { val paths = getSelectedPaths() PropertiesDialog(activity, paths, config.shouldShowHidden) @@ -293,7 +285,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, } } - private fun getCurrentPath() = (thumbnailItems[selectedPositions.first()] as ThumbnailMedium).path + private fun getCurrentPath() = (media[selectedPositions.first()] as ThumbnailMedium).path private fun deleteFiles() { if (selectedPositions.isEmpty()) { @@ -303,22 +295,22 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, val fileDirItems = ArrayList(selectedPositions.size) val removeMedia = ArrayList(selectedPositions.size) - if (thumbnailItems.size <= selectedPositions.first()) { + if (media.size <= selectedPositions.first()) { finishActMode() return } - val SAFPath = (thumbnailItems[selectedPositions.first()] as ThumbnailMedium).path + val SAFPath = (media[selectedPositions.first()] as ThumbnailMedium).path activity.handleSAFDialog(SAFPath) { selectedPositions.sortedDescending().forEach { - val thumbnailItem = thumbnailItems[it] + val thumbnailItem = media[it] if (thumbnailItem is ThumbnailMedium) { fileDirItems.add(FileDirItem(thumbnailItem.path, thumbnailItem.name)) removeMedia.add(thumbnailItem) } } - thumbnailItems.removeAll(removeMedia) + media.removeAll(removeMedia) listener?.tryDeleteFiles(fileDirItems) removeSelectedItems() } @@ -327,19 +319,18 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, private fun getSelectedMedia(): List { val selectedMedia = ArrayList(selectedPositions.size) selectedPositions.forEach { - selectedMedia.add(thumbnailItems[it] as ThumbnailMedium) + selectedMedia.add(media[it] as ThumbnailMedium) } return selectedMedia } private fun getSelectedPaths() = getSelectedMedia().map { it.path } as ArrayList - fun updateMedia(newMedia: ArrayList) { - if (newMedia.hashCode() != currentMediaHash || currentGrouping != config.getFolderGrouping(path)) { + fun updateMedia(newMedia: ArrayList) { + if (newMedia.hashCode() != currentMediaHash) { currentMediaHash = newMedia.hashCode() Handler().postDelayed({ media = newMedia - groupMedia() enableInstantLoad() notifyDataSetChanged() finishActMode() @@ -376,69 +367,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, }, INSTANT_LOAD_DURATION) } - private fun groupMedia() { - currentGrouping = config.getFolderGrouping(path) - if (currentGrouping and GROUP_BY_NONE != 0) { - return - } - - mediumGroups.clear() - media.forEach { - val key = it.getGroupingKey(currentGrouping) - if (!mediumGroups.containsKey(key)) { - mediumGroups[key] = ArrayList() - } - mediumGroups[key]!!.add(it) - } - - val sortDescending = currentGrouping and GROUP_DESCENDING != 0 - val sorted = mediumGroups.toSortedMap(if (sortDescending) compareByDescending { it } else compareBy { it }) - mediumGroups.clear() - sorted.forEach { key, value -> - mediumGroups[key] = value - } - - thumbnailItems.clear() - for ((key, value) in mediumGroups) { - thumbnailItems.add(ThumbnailSection(getFormattedKey(key, currentGrouping))) - value.forEach { - val thumbnailMedium = ThumbnailMedium(it.name, it.path, it.parentPath, it.modified, it.taken, it.size, it.type, it.isFavorite) - thumbnailItems.add(thumbnailMedium) - } - } - } - - private fun getFormattedKey(key: String, grouping: Int): String { - return when { - grouping and GROUP_BY_LAST_MODIFIED != 0 || grouping and GROUP_BY_DATE_TAKEN != 0 -> formatDate(key) - grouping and GROUP_BY_FILE_TYPE != 0 -> getFileTypeString(key) - grouping and GROUP_BY_EXTENSION != 0 -> key.toUpperCase() - grouping and GROUP_BY_FOLDER != 0 -> activity.humanizePath(key) - else -> key - } - } - - private fun formatDate(timestamp: String): String { - return if (timestamp.areDigitsOnly()) { - val cal = Calendar.getInstance(Locale.ENGLISH) - cal.timeInMillis = timestamp.toLong() - DateFormat.format("dd MMM yyyy", cal).toString() - } else { - "" - } - } - - private fun getFileTypeString(key: String): String { - val stringId = when (key.toInt()) { - TYPE_IMAGES -> R.string.images - TYPE_VIDEOS -> R.string.videos - TYPE_GIFS -> R.string.gifs - else -> R.string.raw_images - } - return activity.getString(stringId) - } - - fun getItemBubbleText(position: Int, sorting: Int) = (thumbnailItems[position] as? ThumbnailMedium)?.getBubbleText(sorting) + fun getItemBubbleText(position: Int, sorting: Int) = (media[position] as? ThumbnailMedium)?.getBubbleText(sorting) private fun setupThumbnailMedium(view: View, medium: ThumbnailMedium) { view.apply { diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickMediumDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickMediumDialog.kt index 8a2fb0f4b..58d18bcf4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickMediumDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickMediumDialog.kt @@ -12,13 +12,15 @@ import com.simplemobiletools.gallery.adapters.MediaAdapter import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.getCachedMedia +import com.simplemobiletools.gallery.helpers.MediaFetcher import com.simplemobiletools.gallery.helpers.VIEW_TYPE_GRID import com.simplemobiletools.gallery.models.Medium +import com.simplemobiletools.gallery.models.ThumbnailItem import kotlinx.android.synthetic.main.dialog_medium_picker.view.* class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val callback: (path: String) -> Unit) { var dialog: AlertDialog - var shownMedia = ArrayList() + var shownMedia = ArrayList() val view = activity.layoutInflater.inflate(R.layout.dialog_medium_picker, null) var isGridViewType = activity.config.viewTypeFiles == VIEW_TYPE_GRID @@ -31,7 +33,7 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c dialog = AlertDialog.Builder(activity) .setPositiveButton(R.string.ok, null) .setNegativeButton(R.string.cancel, null) - .setNeutralButton(R.string.other_folder, { dialogInterface, i -> showOtherFolder() }) + .setNeutralButton(R.string.other_folder) { dialogInterface, i -> showOtherFolder() } .create().apply { activity.setupDialogStuff(view, this, R.string.select_photo) } @@ -61,8 +63,8 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c if (media.hashCode() == shownMedia.hashCode()) return - shownMedia = media - val adapter = MediaAdapter(activity, media, null, true, false, view.media_grid, null, path) { + shownMedia = MediaFetcher(activity).groupMedia(media, path) + val adapter = MediaAdapter(activity, shownMedia, null, true, false, view.media_grid, null) { callback((it as Medium).path) dialog.dismiss() } diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaFetcher.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaFetcher.kt index 670523e2e..560f83ca9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaFetcher.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaFetcher.kt @@ -4,14 +4,20 @@ import android.content.Context import android.database.Cursor import android.net.Uri import android.provider.MediaStore +import android.text.format.DateFormat import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* +import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.getDistinctPath import com.simplemobiletools.gallery.extensions.getOTGFolderChildren import com.simplemobiletools.gallery.extensions.shouldFolderBeVisible import com.simplemobiletools.gallery.models.Medium +import com.simplemobiletools.gallery.models.ThumbnailItem +import com.simplemobiletools.gallery.models.ThumbnailMedium +import com.simplemobiletools.gallery.models.ThumbnailSection import java.io.File +import java.util.* class MediaFetcher(val context: Context) { var shouldStop = false @@ -329,4 +335,72 @@ class MediaFetcher(val context: Context) { result }) } + + fun groupMedia(media: ArrayList, path: String): ArrayList { + val thumbnailItems = ArrayList() + val mediumGroups = LinkedHashMap>() + val currentGrouping = context.config.getFolderGrouping(path) + if (currentGrouping and GROUP_BY_NONE != 0) { + media.forEach { + val thumbnailMedium = ThumbnailMedium(it.name, it.path, it.parentPath, it.modified, it.taken, it.size, it.type, it.isFavorite) + thumbnailItems.add(thumbnailMedium) + } + return thumbnailItems + } + + media.forEach { + val key = it.getGroupingKey(currentGrouping) + if (!mediumGroups.containsKey(key)) { + mediumGroups[key] = ArrayList() + } + mediumGroups[key]!!.add(it) + } + + val sortDescending = currentGrouping and GROUP_DESCENDING != 0 + val sorted = mediumGroups.toSortedMap(if (sortDescending) compareByDescending { it } else compareBy { it }) + mediumGroups.clear() + sorted.forEach { key, value -> + mediumGroups[key] = value + } + + for ((key, value) in mediumGroups) { + thumbnailItems.add(ThumbnailSection(getFormattedKey(key, currentGrouping))) + value.forEach { + val thumbnailMedium = ThumbnailMedium(it.name, it.path, it.parentPath, it.modified, it.taken, it.size, it.type, it.isFavorite) + thumbnailItems.add(thumbnailMedium) + } + } + + return thumbnailItems + } + + private fun getFormattedKey(key: String, grouping: Int): String { + return when { + grouping and GROUP_BY_LAST_MODIFIED != 0 || grouping and GROUP_BY_DATE_TAKEN != 0 -> formatDate(key) + grouping and GROUP_BY_FILE_TYPE != 0 -> getFileTypeString(key) + grouping and GROUP_BY_EXTENSION != 0 -> key.toUpperCase() + grouping and GROUP_BY_FOLDER != 0 -> context.humanizePath(key) + else -> key + } + } + + private fun formatDate(timestamp: String): String { + return if (timestamp.areDigitsOnly()) { + val cal = Calendar.getInstance(Locale.ENGLISH) + cal.timeInMillis = timestamp.toLong() + DateFormat.format("dd MMM yyyy", cal).toString() + } else { + "" + } + } + + private fun getFileTypeString(key: String): String { + val stringId = when (key.toInt()) { + TYPE_IMAGES -> R.string.images + TYPE_VIDEOS -> R.string.videos + TYPE_GIFS -> R.string.gifs + else -> R.string.raw_images + } + return context.getString(stringId) + } }