handle media grouping at the MediaActivity

This commit is contained in:
tibbi 2018-06-22 14:54:28 +02:00
parent e759270019
commit 698f9b0c76
4 changed files with 107 additions and 99 deletions

View file

@ -283,8 +283,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
Thread { Thread {
val filtered = mMedia.filter { it.name.contains(text, true) } as ArrayList val filtered = mMedia.filter { it.name.contains(text, true) } as ArrayList
filtered.sortBy { !it.name.startsWith(text, true) } filtered.sortBy { !it.name.startsWith(text, true) }
val groupedMedia = MediaFetcher(applicationContext).groupMedia(filtered, mPath)
runOnUiThread { runOnUiThread {
getMediaAdapter()?.updateMedia(filtered) getMediaAdapter()?.updateMedia(groupedMedia)
} }
}.start() }.start()
} }
@ -315,12 +316,12 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
return return
} }
val media = mMedia.clone() as ArrayList<Medium> val groupedMedia = MediaFetcher(applicationContext).groupMedia(mMedia.clone() as ArrayList<Medium>, mPath)
val currAdapter = media_grid.adapter val currAdapter = media_grid.adapter
if (currAdapter == null) { if (currAdapter == null) {
initZoomListener() initZoomListener()
val fastscroller = if (config.scrollHorizontally) media_horizontal_fastscroller else media_vertical_fastscroller 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) itemClicked((it as ThumbnailMedium).path)
}.apply { }.apply {
setupZoomListener(mZoomListener) setupZoomListener(mZoomListener)
@ -328,8 +329,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
setupLayoutManager() setupLayoutManager()
} else { } else {
(currAdapter as MediaAdapter).updateMedia(media) (currAdapter as MediaAdapter).updateMedia(groupedMedia)
} }
setupScrollDirection() setupScrollDirection()
} }
@ -359,8 +361,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private fun getBubbleTextItem(index: Int, sorting: Int) = getMediaAdapter()?.getItemBubbleText(index, sorting) ?: "" private fun getBubbleTextItem(index: Int, sorting: Int) = getMediaAdapter()?.getItemBubbleText(index, sorting) ?: ""
private fun checkLastMediaChanged() { private fun checkLastMediaChanged() {
if (isActivityDestroyed()) if (isActivityDestroyed()) {
return return
}
mLastMediaHandler.removeCallbacksAndMessages(null) mLastMediaHandler.removeCallbacksAndMessages(null)
mLastMediaHandler.postDelayed({ mLastMediaHandler.postDelayed({

View file

@ -2,7 +2,6 @@ package com.simplemobiletools.gallery.adapters
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.text.format.DateFormat
import android.view.Menu import android.view.Menu
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -19,19 +18,16 @@ import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.dialogs.DeleteWithRememberDialog import com.simplemobiletools.gallery.dialogs.DeleteWithRememberDialog
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.* import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST
import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem import com.simplemobiletools.gallery.models.ThumbnailItem
import com.simplemobiletools.gallery.models.ThumbnailMedium import com.simplemobiletools.gallery.models.ThumbnailMedium
import com.simplemobiletools.gallery.models.ThumbnailSection import com.simplemobiletools.gallery.models.ThumbnailSection
import kotlinx.android.synthetic.main.photo_video_item_grid.view.* import kotlinx.android.synthetic.main.photo_video_item_grid.view.*
import kotlinx.android.synthetic.main.thumbnail_section.view.* import kotlinx.android.synthetic.main.thumbnail_section.view.*
import java.util.*
import kotlin.collections.ArrayList
class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean, class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<ThumbnailItem>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean,
val allowMultiplePicks: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, val path: String, val allowMultiplePicks: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) :
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) { MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private val INSTANT_LOAD_DURATION = 2000L private val INSTANT_LOAD_DURATION = 2000L
private val IMAGE_LOAD_DELAY = 100L private val IMAGE_LOAD_DELAY = 100L
@ -41,13 +37,10 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
private val config = activity.config private val config = activity.config
private val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST private val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST
private var visibleItemPaths = ArrayList<String>() private var visibleItemPaths = ArrayList<String>()
private var thumbnailItems = ArrayList<ThumbnailItem>()
private var loadImageInstantly = false private var loadImageInstantly = false
private var delayHandler = Handler(Looper.getMainLooper()) private var delayHandler = Handler(Looper.getMainLooper())
private var currentMediaHash = media.hashCode() private var currentMediaHash = media.hashCode()
private var currentGrouping = GROUP_BY_NONE
private val hasOTGConnected = activity.hasOTGConnected() private val hasOTGConnected = activity.hasOTGConnected()
private var mediumGroups = LinkedHashMap<String, ArrayList<Medium>>()
private var scrollHorizontally = config.scrollHorizontally private var scrollHorizontally = config.scrollHorizontally
private var animateGifs = config.animateGifs private var animateGifs = config.animateGifs
@ -56,7 +49,6 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
init { init {
setupDragListener(true) setupDragListener(true)
groupMedia()
enableInstantLoad() enableInstantLoad()
} }
@ -84,7 +76,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
} }
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) { override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
val tmbItem = thumbnailItems.getOrNull(position) ?: return val tmbItem = media.getOrNull(position) ?: return
if (tmbItem is ThumbnailMedium) { if (tmbItem is ThumbnailMedium) {
visibleItemPaths.add(tmbItem.path) visibleItemPaths.add(tmbItem.path)
} }
@ -99,10 +91,10 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
bindViewHolder(holder, position, view) bindViewHolder(holder, position, view)
} }
override fun getItemCount() = thumbnailItems.size override fun getItemCount() = media.size
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
val tmbItem = thumbnailItems[position] val tmbItem = media[position]
return if (tmbItem is ThumbnailSection) { return if (tmbItem is ThumbnailSection) {
ITEM_SECTION ITEM_SECTION
} else { } else {
@ -145,7 +137,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
} }
} }
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) override fun getIsItemSelectable(position: Int) = !isASectionTitle(position)
@ -161,7 +153,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
} }
} }
fun isASectionTitle(position: Int) = thumbnailItems.getOrNull(position) is ThumbnailSection fun isASectionTitle(position: Int) = media.getOrNull(position) is ThumbnailSection
private fun checkHideBtnVisibility(menu: Menu) { private fun checkHideBtnVisibility(menu: Menu) {
var hiddenCnt = 0 var hiddenCnt = 0
@ -199,7 +191,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
private fun showProperties() { private fun showProperties() {
if (selectedPositions.size <= 1) { 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 { } else {
val paths = getSelectedPaths() val paths = getSelectedPaths()
PropertiesDialog(activity, paths, config.shouldShowHidden) PropertiesDialog(activity, paths, config.shouldShowHidden)
@ -293,7 +285,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
} }
} }
private fun getCurrentPath() = (thumbnailItems[selectedPositions.first()] as ThumbnailMedium).path private fun getCurrentPath() = (media[selectedPositions.first()] as ThumbnailMedium).path
private fun deleteFiles() { private fun deleteFiles() {
if (selectedPositions.isEmpty()) { if (selectedPositions.isEmpty()) {
@ -303,22 +295,22 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
val fileDirItems = ArrayList<FileDirItem>(selectedPositions.size) val fileDirItems = ArrayList<FileDirItem>(selectedPositions.size)
val removeMedia = ArrayList<ThumbnailMedium>(selectedPositions.size) val removeMedia = ArrayList<ThumbnailMedium>(selectedPositions.size)
if (thumbnailItems.size <= selectedPositions.first()) { if (media.size <= selectedPositions.first()) {
finishActMode() finishActMode()
return return
} }
val SAFPath = (thumbnailItems[selectedPositions.first()] as ThumbnailMedium).path val SAFPath = (media[selectedPositions.first()] as ThumbnailMedium).path
activity.handleSAFDialog(SAFPath) { activity.handleSAFDialog(SAFPath) {
selectedPositions.sortedDescending().forEach { selectedPositions.sortedDescending().forEach {
val thumbnailItem = thumbnailItems[it] val thumbnailItem = media[it]
if (thumbnailItem is ThumbnailMedium) { if (thumbnailItem is ThumbnailMedium) {
fileDirItems.add(FileDirItem(thumbnailItem.path, thumbnailItem.name)) fileDirItems.add(FileDirItem(thumbnailItem.path, thumbnailItem.name))
removeMedia.add(thumbnailItem) removeMedia.add(thumbnailItem)
} }
} }
thumbnailItems.removeAll(removeMedia) media.removeAll(removeMedia)
listener?.tryDeleteFiles(fileDirItems) listener?.tryDeleteFiles(fileDirItems)
removeSelectedItems() removeSelectedItems()
} }
@ -327,19 +319,18 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
private fun getSelectedMedia(): List<ThumbnailMedium> { private fun getSelectedMedia(): List<ThumbnailMedium> {
val selectedMedia = ArrayList<ThumbnailMedium>(selectedPositions.size) val selectedMedia = ArrayList<ThumbnailMedium>(selectedPositions.size)
selectedPositions.forEach { selectedPositions.forEach {
selectedMedia.add(thumbnailItems[it] as ThumbnailMedium) selectedMedia.add(media[it] as ThumbnailMedium)
} }
return selectedMedia return selectedMedia
} }
private fun getSelectedPaths() = getSelectedMedia().map { it.path } as ArrayList<String> private fun getSelectedPaths() = getSelectedMedia().map { it.path } as ArrayList<String>
fun updateMedia(newMedia: ArrayList<Medium>) { fun updateMedia(newMedia: ArrayList<ThumbnailItem>) {
if (newMedia.hashCode() != currentMediaHash || currentGrouping != config.getFolderGrouping(path)) { if (newMedia.hashCode() != currentMediaHash) {
currentMediaHash = newMedia.hashCode() currentMediaHash = newMedia.hashCode()
Handler().postDelayed({ Handler().postDelayed({
media = newMedia media = newMedia
groupMedia()
enableInstantLoad() enableInstantLoad()
notifyDataSetChanged() notifyDataSetChanged()
finishActMode() finishActMode()
@ -376,69 +367,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
}, INSTANT_LOAD_DURATION) }, INSTANT_LOAD_DURATION)
} }
private fun groupMedia() { fun getItemBubbleText(position: Int, sorting: Int) = (media[position] as? ThumbnailMedium)?.getBubbleText(sorting)
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)
private fun setupThumbnailMedium(view: View, medium: ThumbnailMedium) { private fun setupThumbnailMedium(view: View, medium: ThumbnailMedium) {
view.apply { view.apply {

View file

@ -12,13 +12,15 @@ import com.simplemobiletools.gallery.adapters.MediaAdapter
import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.getCachedMedia import com.simplemobiletools.gallery.extensions.getCachedMedia
import com.simplemobiletools.gallery.helpers.MediaFetcher
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_GRID import com.simplemobiletools.gallery.helpers.VIEW_TYPE_GRID
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem
import kotlinx.android.synthetic.main.dialog_medium_picker.view.* import kotlinx.android.synthetic.main.dialog_medium_picker.view.*
class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val callback: (path: String) -> Unit) { class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val callback: (path: String) -> Unit) {
var dialog: AlertDialog var dialog: AlertDialog
var shownMedia = ArrayList<Medium>() var shownMedia = ArrayList<ThumbnailItem>()
val view = activity.layoutInflater.inflate(R.layout.dialog_medium_picker, null) val view = activity.layoutInflater.inflate(R.layout.dialog_medium_picker, null)
var isGridViewType = activity.config.viewTypeFiles == VIEW_TYPE_GRID 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) dialog = AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.setNeutralButton(R.string.other_folder, { dialogInterface, i -> showOtherFolder() }) .setNeutralButton(R.string.other_folder) { dialogInterface, i -> showOtherFolder() }
.create().apply { .create().apply {
activity.setupDialogStuff(view, this, R.string.select_photo) 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()) if (media.hashCode() == shownMedia.hashCode())
return return
shownMedia = media shownMedia = MediaFetcher(activity).groupMedia(media, path)
val adapter = MediaAdapter(activity, media, null, true, false, view.media_grid, null, path) { val adapter = MediaAdapter(activity, shownMedia, null, true, false, view.media_grid, null) {
callback((it as Medium).path) callback((it as Medium).path)
dialog.dismiss() dialog.dismiss()
} }

View file

@ -4,14 +4,20 @@ import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.MediaStore import android.provider.MediaStore
import android.text.format.DateFormat
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.getDistinctPath import com.simplemobiletools.gallery.extensions.getDistinctPath
import com.simplemobiletools.gallery.extensions.getOTGFolderChildren import com.simplemobiletools.gallery.extensions.getOTGFolderChildren
import com.simplemobiletools.gallery.extensions.shouldFolderBeVisible import com.simplemobiletools.gallery.extensions.shouldFolderBeVisible
import com.simplemobiletools.gallery.models.Medium 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.io.File
import java.util.*
class MediaFetcher(val context: Context) { class MediaFetcher(val context: Context) {
var shouldStop = false var shouldStop = false
@ -329,4 +335,72 @@ class MediaFetcher(val context: Context) {
result result
}) })
} }
fun groupMedia(media: ArrayList<Medium>, path: String): ArrayList<ThumbnailItem> {
val thumbnailItems = ArrayList<ThumbnailItem>()
val mediumGroups = LinkedHashMap<String, ArrayList<Medium>>()
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)
}
} }