隐藏缩略图的开关切换
This commit is contained in:
parent
592918d6ce
commit
37e894ad47
@ -7,7 +7,7 @@ import androidx.viewbinding.ViewBinding
|
|||||||
import kotlin.let
|
import kotlin.let
|
||||||
|
|
||||||
abstract class BaseAdapter<K, T : ViewBinding>(
|
abstract class BaseAdapter<K, T : ViewBinding>(
|
||||||
protected val mContext: Context
|
protected val mContext: Context,
|
||||||
) : RecyclerView.Adapter<BaseAdapter.VHolder<T>>() {
|
) : RecyclerView.Adapter<BaseAdapter.VHolder<T>>() {
|
||||||
|
|
||||||
protected val data: MutableList<K> = mutableListOf()
|
protected val data: MutableList<K> = mutableListOf()
|
||||||
@ -28,6 +28,8 @@ abstract class BaseAdapter<K, T : ViewBinding>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getCurrentData() = data
|
||||||
|
|
||||||
override fun getItemCount(): Int = data.size
|
override fun getItemCount(): Int = data.size
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VHolder<T> {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VHolder<T> {
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
package com.ux.video.file.filerecovery.base
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高性能 RecyclerView 通用适配器基类,支持 DiffUtil + ViewBinding
|
||||||
|
*/
|
||||||
|
abstract class DiffBaseAdapter<K, T : ViewBinding>(
|
||||||
|
protected val mContext: Context,
|
||||||
|
diffCallback: DiffUtil.ItemCallback<K>
|
||||||
|
) : ListAdapter<K, DiffBaseAdapter.VHolder<T>>(diffCallback) {
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VHolder<T> {
|
||||||
|
return VHolder(getViewBinding(parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: VHolder<T>, position: Int) {
|
||||||
|
bindItem(holder, getItem(position))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新数据(自动触发 DiffUtil 计算)
|
||||||
|
*/
|
||||||
|
fun submitData(items: List<K>?) {
|
||||||
|
submitList(items ?: emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun getViewBinding(parent: ViewGroup): T
|
||||||
|
protected abstract fun bindItem(holder: VHolder<T>, item: K)
|
||||||
|
|
||||||
|
class VHolder<V : ViewBinding>(val vb: V) : androidx.recyclerview.widget.RecyclerView.ViewHolder(vb.root)
|
||||||
|
}
|
||||||
@ -4,8 +4,11 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.ux.video.file.filerecovery.base.BaseAdapter
|
import com.ux.video.file.filerecovery.base.BaseAdapter
|
||||||
|
import com.ux.video.file.filerecovery.base.DiffBaseAdapter
|
||||||
import com.ux.video.file.filerecovery.databinding.PhotoDisplayDateAdapterBinding
|
import com.ux.video.file.filerecovery.databinding.PhotoDisplayDateAdapterBinding
|
||||||
import com.ux.video.file.filerecovery.utils.Common
|
import com.ux.video.file.filerecovery.utils.Common
|
||||||
import com.ux.video.file.filerecovery.utils.GridSpacingItemDecoration
|
import com.ux.video.file.filerecovery.utils.GridSpacingItemDecoration
|
||||||
@ -15,12 +18,14 @@ class PhotoDisplayDateAdapter(
|
|||||||
mContext: Context,
|
mContext: Context,
|
||||||
var mColumns: Int,
|
var mColumns: Int,
|
||||||
var viewModel: ScanRepository,
|
var viewModel: ScanRepository,
|
||||||
var onSelectedUpdate: (updatePath: String, isAdd: Boolean) -> Unit,
|
var onSelectedUpdate: (resultPhotosFiles: ResultPhotosFiles, isAdd: Boolean) -> Unit,
|
||||||
var clickItem:(item:ResultPhotosFiles)-> Unit
|
var clickItem: (item: ResultPhotosFiles) -> Unit
|
||||||
) :
|
) :
|
||||||
BaseAdapter<Pair<String, List<ResultPhotosFiles>>, PhotoDisplayDateAdapterBinding>(mContext) {
|
BaseAdapter<Pair<String, List<ResultPhotosFiles>>, PhotoDisplayDateAdapterBinding>(mContext) {
|
||||||
|
|
||||||
|
|
||||||
private var allSelected: Boolean? = null
|
private var allSelected: Boolean? = null
|
||||||
|
// private var hideThumbnails = false
|
||||||
override fun getViewBinding(parent: ViewGroup): PhotoDisplayDateAdapterBinding =
|
override fun getViewBinding(parent: ViewGroup): PhotoDisplayDateAdapterBinding =
|
||||||
PhotoDisplayDateAdapterBinding.inflate(
|
PhotoDisplayDateAdapterBinding.inflate(
|
||||||
LayoutInflater.from(parent.context),
|
LayoutInflater.from(parent.context),
|
||||||
@ -28,13 +33,23 @@ class PhotoDisplayDateAdapter(
|
|||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun updateHideThumbnails(isChecked: Boolean) {
|
||||||
|
// hideThumbnails = isChecked
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回所有嵌套的数据量总数
|
* 返回所有嵌套的数据量总数
|
||||||
*/
|
*/
|
||||||
fun getTotalChildCount(): Int {
|
fun getTotalChildCount(hideThumbnails: Boolean): Int {
|
||||||
|
if(hideThumbnails){
|
||||||
|
return data.sumOf { it.second.filter { !it.isThumbnail }.size }
|
||||||
|
}else{
|
||||||
return data.sumOf { it.second.size }
|
return data.sumOf { it.second.size }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* activity页面上点击全选按钮执行
|
* activity页面上点击全选按钮执行
|
||||||
*/
|
*/
|
||||||
@ -42,14 +57,14 @@ class PhotoDisplayDateAdapter(
|
|||||||
allSelected = allSelect
|
allSelected = allSelect
|
||||||
data.forEach {
|
data.forEach {
|
||||||
it.second.forEach { item ->
|
it.second.forEach { item ->
|
||||||
onSelectedUpdate(item.path.toString(),allSelect)
|
onSelectedUpdate(item, allSelect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
allSelected = null
|
allSelected = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetAllValue(b: Boolean?){
|
fun resetAllValue(b: Boolean?) {
|
||||||
allSelected = b
|
allSelected = b
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,26 +74,36 @@ class PhotoDisplayDateAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun bindItem(
|
override fun bindItem(
|
||||||
holder: VHolder<PhotoDisplayDateAdapterBinding>,
|
holder: VHolder<PhotoDisplayDateAdapterBinding>,
|
||||||
item: Pair<String, List<ResultPhotosFiles>>
|
item: Pair<String, List<ResultPhotosFiles>>
|
||||||
) {
|
) {
|
||||||
holder.vb.run {
|
holder.vb.run {
|
||||||
|
|
||||||
item.run {
|
item.run {
|
||||||
val (date, files) = item
|
val (date, files) = item
|
||||||
val childAdapter = PhotoDisplayDateChildAdapter(
|
val childAdapter = PhotoDisplayDateChildAdapter(
|
||||||
mContext,
|
mContext,
|
||||||
mColumns,
|
mColumns,
|
||||||
viewModel,
|
viewModel,
|
||||||
{ path, addOrRemove, isDateAllSelected ->
|
{ resultPhotosFiles, addOrRemove, isDateAllSelected ->
|
||||||
//点击当前Adapter某一天的全选或者子Item上的选中都会回调到这里
|
//点击当前Adapter某一天的全选或者子Item上的选中都会回调到这里
|
||||||
tvDayAllSelect.isSelected = isDateAllSelected
|
tvDayAllSelect.isSelected = isDateAllSelected
|
||||||
onSelectedUpdate(path.toString(),addOrRemove)
|
onSelectedUpdate(resultPhotosFiles, addOrRemove)
|
||||||
},clickItem).apply { setData(files) }
|
}, { updateHideThumbnails->
|
||||||
|
|
||||||
|
tvDayAllSelect.isSelected = updateHideThumbnails
|
||||||
|
|
||||||
|
},clickItem
|
||||||
|
).apply { setData(files) }
|
||||||
|
|
||||||
|
// if (hideThumbnails && files.filter { !it.isThumbnail }.isEmpty()) {
|
||||||
|
// holder.vb.root.isVisible = false
|
||||||
|
// return
|
||||||
|
// }else{
|
||||||
|
// holder.vb.root.isVisible = true
|
||||||
|
// childAdapter.updateHideThumbnails(hideThumbnails)
|
||||||
|
// }
|
||||||
|
|
||||||
allSelected?.let {
|
allSelected?.let {
|
||||||
childAdapter.setAllSelected(it)
|
childAdapter.setAllSelected(it)
|
||||||
@ -90,12 +115,9 @@ class PhotoDisplayDateAdapter(
|
|||||||
|
|
||||||
textDate.text = date
|
textDate.text = date
|
||||||
textChildCounts.text = "(${files.size})"
|
textChildCounts.text = "(${files.size})"
|
||||||
|
|
||||||
recyclerChild.apply {
|
recyclerChild.apply {
|
||||||
layoutManager = GridLayoutManager(context, mColumns)
|
layoutManager = GridLayoutManager(context, mColumns)
|
||||||
val gridSpacingItemDecoration =
|
|
||||||
GridSpacingItemDecoration(mColumns, Common.itemSpacing, Common.horizontalSpacing)
|
|
||||||
Common.showLog("---------mColumns=${mColumns}")
|
|
||||||
// resetItemDecorationOnce(gridSpacingItemDecoration)
|
|
||||||
adapter = childAdapter
|
adapter = childAdapter
|
||||||
isNestedScrollingEnabled = false
|
isNestedScrollingEnabled = false
|
||||||
}
|
}
|
||||||
@ -104,5 +126,21 @@ class PhotoDisplayDateAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ItemDiffCallback : DiffUtil.ItemCallback<Pair<String, List<ResultPhotosFiles>>>() {
|
||||||
|
override fun areItemsTheSame(
|
||||||
|
oldItem: Pair<String, List<ResultPhotosFiles>>,
|
||||||
|
newItem: Pair<String, List<ResultPhotosFiles>>
|
||||||
|
): Boolean {
|
||||||
|
return oldItem.first == newItem.first
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(
|
||||||
|
oldItem: Pair<String, List<ResultPhotosFiles>>,
|
||||||
|
newItem: Pair<String, List<ResultPhotosFiles>>
|
||||||
|
): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.DataSource
|
import com.bumptech.glide.load.DataSource
|
||||||
@ -36,14 +37,18 @@ class PhotoDisplayDateChildAdapter(
|
|||||||
* @param addOrRemove 选中还是取消选中
|
* @param addOrRemove 选中还是取消选中
|
||||||
* @param dateAllSelected 这组数据是否全部选中(某一天)
|
* @param dateAllSelected 这组数据是否全部选中(某一天)
|
||||||
*/
|
*/
|
||||||
var onSelectedUpdate: (updatePath: String, addOrRemove: Boolean, dateAllSelected: Boolean) -> Unit,
|
var onSelectedUpdate: (resultPhotosFiles: ResultPhotosFiles, addOrRemove: Boolean, dateAllSelected: Boolean) -> Unit,
|
||||||
|
var hideThumbnailsUpdate:(dateAllSelected: Boolean)-> Unit,
|
||||||
var clickItem:(item:ResultPhotosFiles)-> Unit
|
var clickItem:(item:ResultPhotosFiles)-> Unit
|
||||||
) :
|
) :
|
||||||
NewBaseAdapter<ResultPhotosFiles>(mContext) {
|
NewBaseAdapter<ResultPhotosFiles>(mContext) {
|
||||||
|
|
||||||
|
// private var hideThumbnails: Boolean? = null
|
||||||
//日期组某一天的数据选择状态维护
|
//日期组某一天的数据选择状态维护
|
||||||
val dateSelectedMap = mutableSetOf<String>()
|
val dateSelectedMap = mutableSetOf<ResultPhotosFiles>()
|
||||||
|
|
||||||
|
//实际显示数据集合(包含隐藏的缩略图)
|
||||||
|
// val visibleList = data.toMutableSet()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TYPE_TWO = 2
|
private const val TYPE_TWO = 2
|
||||||
@ -51,10 +56,17 @@ class PhotoDisplayDateChildAdapter(
|
|||||||
private const val TYPE_FOUR = 4
|
private const val TYPE_FOUR = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fun getVisibleList() = visibleList
|
||||||
|
|
||||||
|
fun updateHideThumbnails(isChecked: Boolean){
|
||||||
|
// hideThumbnails = isChecked
|
||||||
|
notifyDataSetChanged()
|
||||||
|
hideThumbnailsUpdate.invoke(getVisibleCount(dateSelectedMap.toMutableList(),isChecked) == getVisibleCount(data,isChecked))
|
||||||
|
}
|
||||||
|
|
||||||
fun setAllSelected(isAdd: Boolean) {
|
fun setAllSelected(isAdd: Boolean) {
|
||||||
data.forEach {
|
data.forEach {
|
||||||
addOrRemove(it.path.toString(), isAdd)
|
addOrRemove(it, isAdd)
|
||||||
}
|
}
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
@ -120,10 +132,12 @@ class PhotoDisplayDateChildAdapter(
|
|||||||
|
|
||||||
when (holder) {
|
when (holder) {
|
||||||
is TwoHolder -> holder.vb.run {
|
is TwoHolder -> holder.vb.run {
|
||||||
|
// root.isVisible = !(hideThumbnails == true && item.isThumbnail)
|
||||||
initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item)
|
initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ThreeHolder -> holder.vb.run {
|
is ThreeHolder -> holder.vb.run {
|
||||||
|
// root.isVisible = !(hideThumbnails == true && item.isThumbnail)
|
||||||
initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item)
|
initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,17 +159,17 @@ class PhotoDisplayDateChildAdapter(
|
|||||||
item: ResultPhotosFiles
|
item: ResultPhotosFiles
|
||||||
) {
|
) {
|
||||||
item.run {
|
item.run {
|
||||||
viewModel.checkIsSelect(path.toString()).let {
|
viewModel.checkIsSelect(this).let {
|
||||||
imageSelectStatus.isSelected = it
|
imageSelectStatus.isSelected = it
|
||||||
addOrRemove(path.toString(), it)
|
addOrRemove(this, it)
|
||||||
}
|
}
|
||||||
imageSelectStatus.setOnClickListener {
|
imageSelectStatus.setOnClickListener {
|
||||||
it.isSelected = !it.isSelected
|
it.isSelected = !it.isSelected
|
||||||
it.isSelected.let { newStatus ->
|
it.isSelected.let { newStatus ->
|
||||||
addOrRemove(path.toString(), newStatus)
|
addOrRemove(this, newStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textSize.text = Common.formatFileSize(mContext, size)
|
textSize.text = sizeString
|
||||||
|
|
||||||
Glide.with(mContext)
|
Glide.with(mContext)
|
||||||
.load(targetFile)
|
.load(targetFile)
|
||||||
@ -197,14 +211,30 @@ class PhotoDisplayDateChildAdapter(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addOrRemove(path: String, boolean: Boolean) {
|
private fun addOrRemove(resultPhotosFiles: ResultPhotosFiles, boolean: Boolean) {
|
||||||
if (boolean) {
|
if (boolean) {
|
||||||
dateSelectedMap.add(path)
|
dateSelectedMap.add(resultPhotosFiles)
|
||||||
} else {
|
} else {
|
||||||
dateSelectedMap.remove(path)
|
dateSelectedMap.remove(resultPhotosFiles)
|
||||||
}
|
}
|
||||||
onSelectedUpdate.invoke(path, boolean, dateSelectedMap.size == itemCount)
|
onSelectedUpdate.invoke(resultPhotosFiles, boolean, dateSelectedMap.size == data.size)
|
||||||
|
// updateSelected(resultPhotosFiles,boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateSelected(resultPhotosFiles: ResultPhotosFiles,boolean: Boolean){
|
||||||
|
// hideThumbnails?.let {
|
||||||
|
// onSelectedUpdate.invoke(resultPhotosFiles, boolean, getVisibleCount(dateSelectedMap.toMutableList(),it) == getVisibleCount(data,it))
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVisibleCount(list: MutableList<ResultPhotosFiles> = data, hideThumbnails: Boolean): Int {
|
||||||
|
if(hideThumbnails){
|
||||||
|
return list.filter { !it.isThumbnail }.size
|
||||||
|
}else{
|
||||||
|
return list.size
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -17,10 +17,10 @@ import com.ux.video.file.filerecovery.utils.Common.setItemSelect
|
|||||||
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
|
||||||
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterBySize
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterBySize
|
||||||
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterBySizeList
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterBySizeList
|
||||||
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterRemoveThumbnailsAsync
|
||||||
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterThumbnailsAsync
|
||||||
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRange
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRange
|
||||||
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRangeList
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRangeList
|
||||||
//import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinMonths
|
|
||||||
//import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinMonthsList
|
|
||||||
import com.ux.video.file.filerecovery.utils.ExtendFunctions.getParcelableArrayListExtraCompat
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.getParcelableArrayListExtraCompat
|
||||||
import com.ux.video.file.filerecovery.utils.ExtendFunctions.mbToBytes
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.mbToBytes
|
||||||
import com.ux.video.file.filerecovery.utils.GridSpacingItemDecoration
|
import com.ux.video.file.filerecovery.utils.GridSpacingItemDecoration
|
||||||
@ -28,6 +28,7 @@ import com.ux.video.file.filerecovery.utils.ScanManager
|
|||||||
import com.ux.video.file.filerecovery.utils.ScanManager.copySelectedFilesAsync
|
import com.ux.video.file.filerecovery.utils.ScanManager.copySelectedFilesAsync
|
||||||
import com.ux.video.file.filerecovery.utils.ScanManager.deleteFilesAsync
|
import com.ux.video.file.filerecovery.utils.ScanManager.deleteFilesAsync
|
||||||
import com.ux.video.file.filerecovery.utils.ScanRepository
|
import com.ux.video.file.filerecovery.utils.ScanRepository
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
||||||
@ -90,12 +91,16 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
private lateinit var sortByDateReverse: List<Pair<String, List<ResultPhotosFiles>>>
|
private lateinit var sortByDateReverse: List<Pair<String, List<ResultPhotosFiles>>>
|
||||||
private lateinit var sortedByDatePositive: List<Pair<String, List<ResultPhotosFiles>>>
|
private lateinit var sortedByDatePositive: List<Pair<String, List<ResultPhotosFiles>>>
|
||||||
|
|
||||||
|
//最新显示的数据集合(包含缩略图 ,只保存当前筛选后或者排序后显示的数据,不受switch切换影响)
|
||||||
|
private var currentDateList: List<Pair<String, List<ResultPhotosFiles>>>? = null
|
||||||
|
private var currentSizeList: List<ResultPhotosFiles>? = null
|
||||||
|
|
||||||
//选中的所有数据集合(实际选中)
|
//选中的所有数据集合(实际选中)
|
||||||
private lateinit var allSelectedSetList: Set<String>
|
private lateinit var allSelectedSetList: Set<ResultPhotosFiles>
|
||||||
|
|
||||||
//选中的所有数据集合(筛选后的数据实际显示的所有选中)
|
//选中的所有数据集合(筛选后的数据实际显示的所有选中)
|
||||||
private lateinit var filterSelectedSetList: Set<String>
|
private lateinit var filterSelectedSetList: Set<ResultPhotosFiles>
|
||||||
|
|
||||||
private lateinit var mItemDecoration: GridSpacingItemDecoration
|
private lateinit var mItemDecoration: GridSpacingItemDecoration
|
||||||
|
|
||||||
|
|
||||||
@ -124,11 +129,14 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
Common.showLog("当前显示筛选数据 选中状态更新: ${displaySet.size}")
|
Common.showLog("当前显示筛选数据 选中状态更新: ${displaySet.size}")
|
||||||
updateCurrentIsAllSelectStatus()
|
updateCurrentIsAllSelectStatus()
|
||||||
}
|
}
|
||||||
binding.imageViewBack.setOnClickListener { finish() }
|
|
||||||
list?.let {
|
list?.let {
|
||||||
|
binding.tvThumbnailCounts.text =
|
||||||
|
getString(R.string.hide_thumbnails, it.filter { it.isThumbnail }.size)
|
||||||
|
|
||||||
|
|
||||||
//降序(最近的在前面)
|
//降序(最近的在前面)
|
||||||
sortByDateReverse = Common.getSortByDayNewToOld(it)
|
sortByDateReverse = Common.getSortByDayNewToOldInit(it)
|
||||||
//升序(时间最远的在前面)
|
//升序(时间最远的在前面)
|
||||||
sortedByDatePositive = Common.getSortByDayOldToNew(sortByDateReverse)
|
sortedByDatePositive = Common.getSortByDayOldToNew(sortByDateReverse)
|
||||||
sortBySizeBigToSmall = Common.getSortBySizeBigToSmall(it)
|
sortBySizeBigToSmall = Common.getSortBySizeBigToSmall(it)
|
||||||
@ -137,8 +145,11 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
sizeSortAdapter = PhotoDisplayDateChildAdapter(
|
sizeSortAdapter = PhotoDisplayDateChildAdapter(
|
||||||
this@PhotoSortingActivity,
|
this@PhotoSortingActivity,
|
||||||
columns, viewModel,
|
columns, viewModel,
|
||||||
{ path, isAdd, allSelected ->
|
{ resultPhotosFiles, isAdd, allSelected ->
|
||||||
viewModel.toggleSelection(isAdd, path)
|
viewModel.toggleSelection(isAdd, resultPhotosFiles)
|
||||||
|
}, { hide ->
|
||||||
|
|
||||||
|
|
||||||
}) { item ->
|
}) { item ->
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent(
|
Intent(
|
||||||
@ -166,10 +177,47 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
})
|
})
|
||||||
}.apply {
|
}.apply {
|
||||||
setData(sortByDateReverse)
|
setData(sortByDateReverse)
|
||||||
|
resetCurrentDateList(sortByDateReverse)
|
||||||
}
|
}
|
||||||
setDateAdapter()
|
setDateAdapter()
|
||||||
setFilter()
|
setFilter()
|
||||||
binding.run {
|
binding.run {
|
||||||
|
imageViewBack.setOnClickListener { finish() }
|
||||||
|
switchHideThumbnails.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
when (recyclerView.adapter) {
|
||||||
|
is PhotoDisplayDateAdapter -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
dateAdapter?.run {
|
||||||
|
initGetCurrentDateList().let { list->
|
||||||
|
val filterThumbnailsAsync =
|
||||||
|
if (isChecked) list.filterThumbnailsAsync() else list
|
||||||
|
setData(filterThumbnailsAsync)
|
||||||
|
checkRefreshDisPlaySelected(list1 = filterThumbnailsAsync)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
is PhotoDisplayDateChildAdapter -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
sizeSortAdapter?.run {
|
||||||
|
initGetCurrentSizeList().let {
|
||||||
|
val filterThumbnailsAsync =
|
||||||
|
if (isChecked) it.filterRemoveThumbnailsAsync() else it
|
||||||
|
setData(filterThumbnailsAsync)
|
||||||
|
checkRefreshDisPlaySelected(list2 = filterThumbnailsAsync)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateCurrentIsAllSelectStatus()
|
||||||
|
|
||||||
|
}
|
||||||
tvRecover.setOnClickListener {
|
tvRecover.setOnClickListener {
|
||||||
showRecoveringDialog()
|
showRecoveringDialog()
|
||||||
lifecycleScope.copySelectedFilesAsync(
|
lifecycleScope.copySelectedFilesAsync(
|
||||||
@ -209,29 +257,66 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
when (it) {
|
when (it) {
|
||||||
SORT_ASC_DATE -> {
|
SORT_ASC_DATE -> {
|
||||||
setDateAdapter()
|
setDateAdapter()
|
||||||
dateAdapter?.setData(sortedByDatePositive)
|
lifecycleScope.launch {
|
||||||
|
initGetCurrentDateList().let {
|
||||||
|
val filterThumbnailsAsync =
|
||||||
|
if (switchHideThumbnails.isChecked) it.filterThumbnailsAsync() else it
|
||||||
|
val sortByDayOldToNew =
|
||||||
|
Common.getSortByDayOldToNew(filterThumbnailsAsync)
|
||||||
|
dateAdapter?.setData(sortByDayOldToNew)
|
||||||
|
resetCurrentDateList(sortByDayOldToNew)
|
||||||
|
}
|
||||||
sortReverse = false
|
sortReverse = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
SORT_DESC_DATE -> {
|
SORT_DESC_DATE -> {
|
||||||
setDateAdapter()
|
setDateAdapter()
|
||||||
dateAdapter?.setData(sortByDateReverse)
|
lifecycleScope.launch {
|
||||||
|
initGetCurrentDateList().let {
|
||||||
|
val filterThumbnailsAsync =
|
||||||
|
if (switchHideThumbnails.isChecked) it.filterThumbnailsAsync() else it
|
||||||
|
val sortByDayNewToOld = Common.getSortByDayNewToOld(filterThumbnailsAsync)
|
||||||
|
dateAdapter?.setData(sortByDayNewToOld)
|
||||||
|
resetCurrentDateList(sortByDayNewToOld)
|
||||||
|
}
|
||||||
sortReverse = true
|
sortReverse = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SORT_DESC_SIZE -> {
|
SORT_DESC_SIZE -> {
|
||||||
setSizeAdapter()
|
setSizeAdapter()
|
||||||
|
lifecycleScope.launch {
|
||||||
|
initGetCurrentSizeList().let {
|
||||||
|
val filterThumbnailsAsync =
|
||||||
|
if (switchHideThumbnails.isChecked) it.filterRemoveThumbnailsAsync() else it
|
||||||
|
val sortBySizeBigToSmall =
|
||||||
|
Common.getSortBySizeBigToSmall(filterThumbnailsAsync)
|
||||||
sizeSortAdapter?.setData(sortBySizeBigToSmall)
|
sizeSortAdapter?.setData(sortBySizeBigToSmall)
|
||||||
|
resetCurrentSizeList(sortBySizeBigToSmall)
|
||||||
|
}
|
||||||
sortReverse = true
|
sortReverse = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
SORT_ASC_SIZE -> {
|
SORT_ASC_SIZE -> {
|
||||||
setSizeAdapter()
|
setSizeAdapter()
|
||||||
|
lifecycleScope.launch {
|
||||||
|
initGetCurrentSizeList().let {
|
||||||
|
val filterThumbnailsAsync =
|
||||||
|
if (switchHideThumbnails.isChecked) it.filterRemoveThumbnailsAsync() else it
|
||||||
|
val sortBySizeSmallToBig =
|
||||||
|
Common.getSortBySizeSmallToBig(filterThumbnailsAsync)
|
||||||
sizeSortAdapter?.setData(sortBySizeSmallToBig)
|
sizeSortAdapter?.setData(sortBySizeSmallToBig)
|
||||||
|
resetCurrentSizeList(sortBySizeSmallToBig)
|
||||||
|
}
|
||||||
sortReverse = false
|
sortReverse = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
sortDialogFragment?.show(supportFragmentManager, "")
|
sortDialogFragment?.show(supportFragmentManager, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,25 +339,50 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
when (binding.recyclerView.adapter) {
|
when (binding.recyclerView.adapter) {
|
||||||
is PhotoDisplayDateAdapter -> {
|
is PhotoDisplayDateAdapter -> {
|
||||||
val adapter = binding.recyclerView.adapter as PhotoDisplayDateAdapter
|
val adapter = binding.recyclerView.adapter as PhotoDisplayDateAdapter
|
||||||
binding.tvSelectAll.isSelected = it == adapter.getTotalChildCount()
|
binding.tvSelectAll.isSelected = it == adapter.getTotalChildCount(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
is PhotoDisplayDateChildAdapter -> {
|
is PhotoDisplayDateChildAdapter -> {
|
||||||
binding.tvSelectAll.isSelected = it == binding.recyclerView.adapter?.itemCount
|
val adapter = binding.recyclerView.adapter as PhotoDisplayDateChildAdapter
|
||||||
}
|
binding.tvSelectAll.isSelected =
|
||||||
|
it == adapter.itemCount
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initGetCurrentSizeList(): List<ResultPhotosFiles> {
|
||||||
|
currentSizeList = currentSizeList ?: currentDateList?.flatMap { it.second }
|
||||||
|
return currentSizeList!!
|
||||||
|
}
|
||||||
|
private fun resetCurrentSizeList(currentList: List<ResultPhotosFiles>) {
|
||||||
|
currentSizeList = currentList
|
||||||
|
currentDateList = null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initGetCurrentDateList(): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||||
|
currentDateList = currentDateList ?: Common.getSortByDayNewToOldInit(currentSizeList!!)
|
||||||
|
return currentDateList!!
|
||||||
|
}
|
||||||
|
private fun resetCurrentDateList(currentList: List<Pair<String, List<ResultPhotosFiles>>> ) {
|
||||||
|
currentDateList = currentList
|
||||||
|
currentSizeList = null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置适配器 日期显示和大小显示适配器切换
|
||||||
|
*/
|
||||||
private fun setDateAdapter() {
|
private fun setDateAdapter() {
|
||||||
binding.recyclerView.run {
|
binding.recyclerView.run {
|
||||||
adapter = dateAdapter?.apply { setColumns(columns) }
|
adapter = dateAdapter?.apply { setColumns(columns) }
|
||||||
layoutManager = LinearLayoutManager(this@PhotoSortingActivity)
|
layoutManager = LinearLayoutManager(this@PhotoSortingActivity)
|
||||||
val bPx = 16.dpToPx(context)
|
setPadding(0, 0, 0, 70.dpToPx(context))
|
||||||
setPadding(0, 0, 0, 0)
|
|
||||||
clipToPadding = false
|
clipToPadding = false
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +390,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
binding.recyclerView.run {
|
binding.recyclerView.run {
|
||||||
val aPx = 16.dpToPx(context)
|
val aPx = 16.dpToPx(context)
|
||||||
val bPx = 6.dpToPx(context)
|
val bPx = 6.dpToPx(context)
|
||||||
setPadding(aPx, 0, bPx, 0)
|
setPadding(aPx, 0, bPx, 70.dpToPx(context))
|
||||||
clipToPadding = false
|
clipToPadding = false
|
||||||
layoutManager = GridLayoutManager(context, columns)
|
layoutManager = GridLayoutManager(context, columns)
|
||||||
adapter = sizeSortAdapter
|
adapter = sizeSortAdapter
|
||||||
@ -293,6 +403,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
*/
|
*/
|
||||||
private fun setFilter() {
|
private fun setFilter() {
|
||||||
//日期筛选
|
//日期筛选
|
||||||
|
|
||||||
binding.run {
|
binding.run {
|
||||||
filterDateLayout.setOnClickListener {
|
filterDateLayout.setOnClickListener {
|
||||||
setItemSelect(it as LinearLayout, true)
|
setItemSelect(it as LinearLayout, true)
|
||||||
@ -304,40 +415,45 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
when (clickValue) {
|
when (clickValue) {
|
||||||
data[0] -> {
|
data[0] -> {
|
||||||
filterDate = FILTER_DATE_ALL
|
filterDate = FILTER_DATE_ALL
|
||||||
|
titleDate.text = clickValue
|
||||||
startFilter()
|
startFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
data[1] -> {
|
data[1] -> {
|
||||||
filterDate = FILTER_DATE_1
|
filterDate = FILTER_DATE_1
|
||||||
|
titleDate.text = clickValue
|
||||||
startFilter()
|
startFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
data[2] -> {
|
data[2] -> {
|
||||||
filterDate = FILTER_DATE_6
|
filterDate = FILTER_DATE_6
|
||||||
|
titleDate.text = clickValue
|
||||||
startFilter()
|
startFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
data[3] -> {
|
data[3] -> {
|
||||||
filterDate = FILTER_DATE_24
|
filterDate = FILTER_DATE_24
|
||||||
|
titleDate.text = clickValue
|
||||||
startFilter()
|
startFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
data[4] -> {
|
data[4] -> {
|
||||||
filterDate = FILTER_DATE_CUSTOMER
|
filterDate = FILTER_DATE_CUSTOMER
|
||||||
if (showDialog)
|
if (showDialog)
|
||||||
showStartDateDialog(true,null)
|
showStartDateDialog(true, null)
|
||||||
else {
|
else {
|
||||||
|
titleDate.text = clickValue
|
||||||
startFilter()
|
startFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}, onResetDate = { isStart,currentDate ->
|
}, onResetDate = { isStart, currentDate ->
|
||||||
if (isStart) {
|
if (isStart) {
|
||||||
showStartDateDialog(false,currentDate)
|
showStartDateDialog(false, currentDate)
|
||||||
} else {
|
} else {
|
||||||
showEndDateDialog(false,currentDate)
|
showEndDateDialog(false, currentDate)
|
||||||
}
|
}
|
||||||
|
|
||||||
}) {
|
}) {
|
||||||
@ -360,6 +476,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
data,
|
data,
|
||||||
0,
|
0,
|
||||||
{ clickValue ->
|
{ clickValue ->
|
||||||
|
titleSize.text = clickValue
|
||||||
when (clickValue) {
|
when (clickValue) {
|
||||||
data[0] -> filterSize = FILTER_SIZE_ALL
|
data[0] -> filterSize = FILTER_SIZE_ALL
|
||||||
data[1] -> filterSize = FILTER_SIZE_1
|
data[1] -> filterSize = FILTER_SIZE_1
|
||||||
@ -430,8 +547,6 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
tvDelete.text = getString(R.string.delete_placeholder, selectedCounts)
|
tvDelete.text = getString(R.string.delete_placeholder, selectedCounts)
|
||||||
tvRecover.text = getString(R.string.recover_placeholder, selectedCounts)
|
tvRecover.text = getString(R.string.recover_placeholder, selectedCounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -452,15 +567,10 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
)
|
)
|
||||||
.filterBySize(filterSizeCovert.first, filterSizeCovert.second)
|
.filterBySize(filterSizeCovert.first, filterSizeCovert.second)
|
||||||
.let { currentList ->
|
.let { currentList ->
|
||||||
//对筛选后的数据与实际选中集合对比 ,得出筛选后显示的数据中的选中数据
|
checkRefreshDisPlaySelected(list1 = currentList)
|
||||||
val checkSelectListContain =
|
|
||||||
Common.checkSelectListContainDate(currentList, allSelectedSetList)
|
|
||||||
updateButtonCounts(checkSelectListContain.first)
|
|
||||||
viewModel.filterResetDisplayFlow(checkSelectListContain.second)
|
|
||||||
|
|
||||||
Common.showLog("筛选后重置 allSelectedSetList=${allSelectedSetList.size} filterSelectedSetList=${filterSelectedSetList.size} Thread=${Thread.currentThread().name}")
|
|
||||||
dateAdapter?.resetAllValue(null)
|
dateAdapter?.resetAllValue(null)
|
||||||
dateAdapter?.setData(currentList)
|
dateAdapter?.setData(currentList)
|
||||||
|
resetCurrentDateList(currentList)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,31 +586,58 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
)
|
)
|
||||||
.filterBySizeList(filterSizeCovert.first, filterSizeCovert.second)
|
.filterBySizeList(filterSizeCovert.first, filterSizeCovert.second)
|
||||||
.let { currentList ->
|
.let { currentList ->
|
||||||
//对筛选后的数据与实际选中集合对比 ,得出筛选后显示的数据中的选中数据
|
checkRefreshDisPlaySelected(list2 = currentList)
|
||||||
val checkSelectListContain =
|
|
||||||
Common.checkSelectListContainSize(currentList, allSelectedSetList)
|
|
||||||
updateButtonCounts(checkSelectListContain.first)
|
|
||||||
viewModel.filterResetDisplayFlow(checkSelectListContain.second)
|
|
||||||
sizeSortAdapter?.setData(currentList)
|
sizeSortAdapter?.setData(currentList)
|
||||||
|
resetCurrentSizeList(currentList)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据筛选或者缩略图切换显示后,对比刷新实际显示的选中数据
|
||||||
|
*/
|
||||||
|
private fun checkRefreshDisPlaySelected(
|
||||||
|
list1: List<Pair<String, List<ResultPhotosFiles>>>? = null,
|
||||||
|
list2: List<ResultPhotosFiles>? = null
|
||||||
|
) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
list1?.let {
|
||||||
|
val checkSelectListContain =
|
||||||
|
Common.checkSelectListContainDateAsync(it, allSelectedSetList)
|
||||||
|
viewModel.filterResetDisplayFlow(checkSelectListContain.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
list2?.let {
|
||||||
|
val checkSelectListContain =
|
||||||
|
Common.checkSelectListContainSize(it, allSelectedSetList)
|
||||||
|
viewModel.filterResetDisplayFlow(checkSelectListContain.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun filterSizeCovert(filterSize: Int): Pair<Long, Long> {
|
private fun filterSizeCovert(filterSize: Int): Pair<Long, Long> {
|
||||||
return when (filterSize) {
|
return when (filterSize) {
|
||||||
FILTER_SIZE_ALL -> Pair(-1L, -1L)
|
FILTER_SIZE_ALL -> Pair(-1L, -1L)
|
||||||
FILTER_SIZE_1 -> Pair(0L, 1.mbToBytes())
|
FILTER_SIZE_1 -> Pair(0L, 1.mbToBytes())
|
||||||
FILTER_SIZE_5 -> Pair(1L, 5.mbToBytes())
|
FILTER_SIZE_5 -> Pair(1.mbToBytes(), 5.mbToBytes())
|
||||||
FILTER_SIZE_OVER_5 -> Pair(5.mbToBytes(), Long.MAX_VALUE)
|
FILTER_SIZE_OVER_5 -> Pair(5.mbToBytes(), Long.MAX_VALUE)
|
||||||
else -> Pair(-1L, -1L)
|
else -> Pair(-1L, -1L)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
private fun showStartDateDialog(isNeedSetSelected: Boolean,currentDate: Date?) {
|
* 显示筛选开始日期弹窗
|
||||||
|
*/
|
||||||
|
private fun showStartDateDialog(isNeedSetSelected: Boolean, currentDate: Date?) {
|
||||||
|
|
||||||
dialogCustomerDateStart = dialogCustomerDateStart ?: DatePickerDialogFragment(
|
dialogCustomerDateStart = dialogCustomerDateStart ?: DatePickerDialogFragment(
|
||||||
this,
|
this,
|
||||||
@ -514,16 +651,9 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
filterStartDate = selectedDate
|
filterStartDate = selectedDate
|
||||||
// filterDatePopupWindows?.updateStartEndDate(start = selectedDate)
|
// filterDatePopupWindows?.updateStartEndDate(start = selectedDate)
|
||||||
Log.d("showStartDateDialog", "isFirst=${isNeedSetSelected}--------")
|
Log.d("showStartDateDialog", "isFirst=${isNeedSetSelected}--------")
|
||||||
showEndDateDialog(isNeedSetSelected,null)
|
showEndDateDialog(isNeedSetSelected, null)
|
||||||
|
|
||||||
|
|
||||||
// if (isFirst) {
|
|
||||||
// showEndDateDialog(true)
|
|
||||||
// } else {
|
|
||||||
// if (filterDate == FILTER_DATE_CUSTOMER) {
|
|
||||||
// startFilter()
|
|
||||||
// filterDatePopupWindows?.dismiss()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
onClickCancel = {
|
onClickCancel = {
|
||||||
|
|
||||||
@ -534,7 +664,10 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showEndDateDialog(isNeedSetSelected: Boolean,currentDate: Date?) {
|
/**
|
||||||
|
* 显示筛选结束日期弹窗
|
||||||
|
*/
|
||||||
|
private fun showEndDateDialog(isNeedSetSelected: Boolean, currentDate: Date?) {
|
||||||
dialogCustomerDateEnd = dialogCustomerDateEnd ?: DatePickerDialogFragment(
|
dialogCustomerDateEnd = dialogCustomerDateEnd ?: DatePickerDialogFragment(
|
||||||
this,
|
this,
|
||||||
getString(R.string.end_date),
|
getString(R.string.end_date),
|
||||||
@ -546,12 +679,13 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
filterStartDate?.let { setRangeDate(it) }
|
filterStartDate?.let { setRangeDate(it) }
|
||||||
onPickerChooseListener = { selectedDate ->
|
onPickerChooseListener = { selectedDate ->
|
||||||
filterEndDate = selectedDate
|
filterEndDate = selectedDate
|
||||||
if(isNeedSetSelected){
|
if (isNeedSetSelected) {
|
||||||
filterDatePopupWindows?.updateSelectPos(4, filterStartDate!!, filterEndDate!!)
|
filterDatePopupWindows?.updateSelectPos(4, filterStartDate!!, filterEndDate!!)
|
||||||
startFilter()
|
startFilter()
|
||||||
filterDatePopupWindows?.dismiss()
|
filterDatePopupWindows?.dismiss()
|
||||||
}else{
|
binding.titleDate.text = resources.getStringArray(R.array.filter_date)[4]
|
||||||
filterDatePopupWindows?.updateStartEndDate(filterStartDate,filterEndDate)
|
} else {
|
||||||
|
filterDatePopupWindows?.updateStartEndDate(filterStartDate, filterEndDate)
|
||||||
if (filterDate == FILTER_DATE_CUSTOMER) {
|
if (filterDate == FILTER_DATE_CUSTOMER) {
|
||||||
startFilter()
|
startFilter()
|
||||||
filterDatePopupWindows?.dismiss()
|
filterDatePopupWindows?.dismiss()
|
||||||
@ -566,6 +700,10 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示恢复中弹窗
|
||||||
|
*/
|
||||||
private fun showRecoveringDialog() {
|
private fun showRecoveringDialog() {
|
||||||
dialogRecovering =
|
dialogRecovering =
|
||||||
dialogRecovering ?: RecoveringDialogFragment(filterSelectedSetList.size) {
|
dialogRecovering ?: RecoveringDialogFragment(filterSelectedSetList.size) {
|
||||||
@ -574,6 +712,10 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
|||||||
dialogRecovering?.show(supportFragmentManager, "")
|
dialogRecovering?.show(supportFragmentManager, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示删除中弹窗
|
||||||
|
*/
|
||||||
private fun showDeletingDialog() {
|
private fun showDeletingDialog() {
|
||||||
dialogDeleting = dialogDeleting ?: DeletingDialogFragment(filterSelectedSetList.size) {
|
dialogDeleting = dialogDeleting ?: DeletingDialogFragment(filterSelectedSetList.size) {
|
||||||
complete()
|
complete()
|
||||||
|
|||||||
@ -10,9 +10,23 @@ data class ResultPhotosFiles(
|
|||||||
val name: String,
|
val name: String,
|
||||||
val path: String? = null,
|
val path: String? = null,
|
||||||
val size: Long, // 字节
|
val size: Long, // 字节
|
||||||
|
val sizeString: String,
|
||||||
val lastModified: Long, // 时间戳
|
val lastModified: Long, // 时间戳
|
||||||
var resolution: String // 尺寸
|
var resolution: String // 尺寸
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val targetFile: File?
|
val targetFile: File?
|
||||||
get() = path?.let { File(it) }
|
get() = path?.let { File(it) }
|
||||||
|
|
||||||
|
//是否为缩略图文件(宽高任一小于 256)
|
||||||
|
val isThumbnail: Boolean
|
||||||
|
get() {
|
||||||
|
val parts = resolution.lowercase().split("*").mapNotNull {
|
||||||
|
it.trim().toIntOrNull()
|
||||||
|
}
|
||||||
|
if (parts.size == 2) {
|
||||||
|
val (width, height) = parts
|
||||||
|
return width < 256 || height < 256
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -111,7 +111,7 @@ class ScanningActivity : BaseActivity<ActivityScanningBinding>() {
|
|||||||
val total = 800
|
val total = 800
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val root = Environment.getExternalStorageDirectory()
|
val root = Environment.getExternalStorageDirectory()
|
||||||
ScanManager.scanAllDocuments(root, type = scanType).flowOn(Dispatchers.IO).collect {
|
ScanManager.scanAllDocuments(this@ScanningActivity,root, type = scanType).flowOn(Dispatchers.IO).collect {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ScanState.Progress -> {
|
is ScanState.Progress -> {
|
||||||
updateProgress(it)
|
updateProgress(it)
|
||||||
@ -131,7 +131,7 @@ class ScanningActivity : BaseActivity<ActivityScanningBinding>() {
|
|||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
|
||||||
val root = Environment.getExternalStorageDirectory()
|
val root = Environment.getExternalStorageDirectory()
|
||||||
ScanManager.scanHiddenPhotoAsync(root, type = scanType).flowOn(Dispatchers.IO).collect {
|
ScanManager.scanHiddenPhotoAsync(this@ScanningActivity,root, type = scanType).flowOn(Dispatchers.IO).collect {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ScanState.Progress -> {
|
is ScanState.Progress -> {
|
||||||
updateProgress(it)
|
updateProgress(it)
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import com.ux.video.file.filerecovery.App
|
|||||||
import com.ux.video.file.filerecovery.R
|
import com.ux.video.file.filerecovery.R
|
||||||
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
|
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
|
||||||
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
|
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.collections.sortedBy
|
import kotlin.collections.sortedBy
|
||||||
@ -40,9 +42,7 @@ object Common {
|
|||||||
/**
|
/**
|
||||||
* 默认按照日期分类,将最新的排前面 降序
|
* 默认按照日期分类,将最新的排前面 降序
|
||||||
*/
|
*/
|
||||||
fun getSortByDayNewToOld(list: ArrayList<ResultPhotosFiles>): List<Pair<String, List<ResultPhotosFiles>>> {
|
fun getSortByDayNewToOldInit(list: List<ResultPhotosFiles>): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||||
|
|
||||||
|
|
||||||
val grouped = list.groupBy {
|
val grouped = list.groupBy {
|
||||||
dateFormat.format(Date(it.lastModified))
|
dateFormat.format(Date(it.lastModified))
|
||||||
}
|
}
|
||||||
@ -52,6 +52,16 @@ object Common {
|
|||||||
return parentData
|
return parentData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSortByDayNewToOld(
|
||||||
|
list: List<Pair<String, List<ResultPhotosFiles>>>
|
||||||
|
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||||
|
return list.sortedByDescending { pair ->
|
||||||
|
dateFormat.parse(pair.first)?.time ?: 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按照日期排序, 时间最早的排前面 升序
|
* 按照日期排序, 时间最早的排前面 升序
|
||||||
*
|
*
|
||||||
@ -62,14 +72,14 @@ object Common {
|
|||||||
/**
|
/**
|
||||||
* 按照文件大小排序,将最大的排前面 降序
|
* 按照文件大小排序,将最大的排前面 降序
|
||||||
*/
|
*/
|
||||||
fun getSortBySizeBigToSmall(list: ArrayList<ResultPhotosFiles>) = list.sortedByDescending {
|
fun getSortBySizeBigToSmall(list: List<ResultPhotosFiles>) = list.sortedByDescending {
|
||||||
it.size
|
it.size
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按照文件大小排序,将最小的排前面 升序
|
* 按照文件大小排序,将最小的排前面 升序
|
||||||
*/
|
*/
|
||||||
fun getSortBySizeSmallToBig(list: ArrayList<ResultPhotosFiles>) = list.sortedBy {
|
fun getSortBySizeSmallToBig(list: List<ResultPhotosFiles>) = list.sortedBy {
|
||||||
it.size
|
it.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,16 +91,19 @@ object Common {
|
|||||||
if (size < 1024) {
|
if (size < 1024) {
|
||||||
return "$size B"
|
return "$size B"
|
||||||
}
|
}
|
||||||
|
|
||||||
val kb = size / 1024.0
|
val kb = size / 1024.0
|
||||||
if (kb < 1024) {
|
if (kb < 1024) {
|
||||||
return String.format(context.getString(R.string.size_kb), kb)
|
return String.format(Locale.CHINA, context.getString(R.string.size_kb), kb)
|
||||||
}
|
}
|
||||||
|
|
||||||
val mb = kb / 1024.0
|
val mb = kb / 1024.0
|
||||||
if (mb < 1024) {
|
if (mb < 1024) {
|
||||||
return String.format(context.getString(R.string.size_kb), mb)
|
return String.format(Locale.CHINA, context.getString(R.string.size_mb), mb)
|
||||||
}
|
}
|
||||||
|
|
||||||
val gb = mb / 1024.0
|
val gb = mb / 1024.0
|
||||||
return String.format(context.getString(R.string.size_gb), gb)
|
return String.format(Locale.CHINA, context.getString(R.string.size_gb), gb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -130,12 +143,7 @@ object Common {
|
|||||||
/**
|
/**
|
||||||
* 设置全部子View的选中
|
* 设置全部子View的选中
|
||||||
*/
|
*/
|
||||||
// fun setItemSelect(view: ViewGroup, boolean: Boolean) {
|
|
||||||
// for (i in 0 until view.childCount) {
|
|
||||||
// val child = view.getChildAt(i)
|
|
||||||
// child.isSelected = boolean
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
fun setItemSelect(view: View, selected: Boolean) {
|
fun setItemSelect(view: View, selected: Boolean) {
|
||||||
view.isSelected = selected
|
view.isSelected = selected
|
||||||
if (view is ViewGroup) {
|
if (view is ViewGroup) {
|
||||||
@ -154,14 +162,14 @@ object Common {
|
|||||||
*/
|
*/
|
||||||
fun checkSelectListContainDate(
|
fun checkSelectListContainDate(
|
||||||
list: List<Pair<String, List<ResultPhotosFiles>>>,
|
list: List<Pair<String, List<ResultPhotosFiles>>>,
|
||||||
selected: Set<String>
|
selected: Set<ResultPhotosFiles>
|
||||||
): Pair<Int, MutableSet<String>> {
|
): Pair<Int, MutableSet<ResultPhotosFiles>> {
|
||||||
val currentSelected = mutableSetOf<String>()
|
val currentSelected = mutableSetOf<ResultPhotosFiles>()
|
||||||
|
|
||||||
val totalSelectedCount = list.sumOf { pair ->
|
val totalSelectedCount = list.sumOf { pair ->
|
||||||
pair.second.count {
|
pair.second.count {
|
||||||
val isSelected = it.path in selected
|
val isSelected = it in selected
|
||||||
if (isSelected) currentSelected.add(it.path.toString())
|
if (isSelected) currentSelected.add(it)
|
||||||
isSelected
|
isSelected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,20 +178,74 @@ object Common {
|
|||||||
return totalSelectedCount to currentSelected
|
return totalSelectedCount to currentSelected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun checkSelectListContainDateAsync(
|
||||||
|
list: List<Pair<String, List<ResultPhotosFiles>>>,
|
||||||
|
selected: Set<ResultPhotosFiles>
|
||||||
|
): Pair<Int, MutableSet<ResultPhotosFiles>> = withContext(Dispatchers.Default) {
|
||||||
|
val currentSelected = mutableSetOf<ResultPhotosFiles>()
|
||||||
|
var totalSelectedCount = 0
|
||||||
|
|
||||||
fun checkSelectListContainSize(
|
// 高效遍历外层 + 内层列表
|
||||||
|
for (pair in list) {
|
||||||
|
for (file in pair.second) {
|
||||||
|
if (file in selected) {
|
||||||
|
totalSelectedCount++
|
||||||
|
currentSelected.add(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSelectedCount to currentSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fun checkSelectListContainSize(
|
||||||
|
// list: List<ResultPhotosFiles>,
|
||||||
|
// selected: Set<String>
|
||||||
|
// ): Pair<Int, MutableSet<String>> {
|
||||||
|
// val currentSelected = mutableSetOf<String>()
|
||||||
|
// val totalSelectedCount = list.count {
|
||||||
|
// val isSelected = it.path in selected
|
||||||
|
// if (isSelected) currentSelected.add(it.path.toString())
|
||||||
|
// isSelected
|
||||||
|
// }
|
||||||
|
// showLog("-------totalSelectedCount 222=${totalSelectedCount}")
|
||||||
|
// return totalSelectedCount to currentSelected
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun checkSelectListContainSize(
|
||||||
list: List<ResultPhotosFiles>,
|
list: List<ResultPhotosFiles>,
|
||||||
selected: Set<String>
|
selected: Set<ResultPhotosFiles>
|
||||||
): Pair<Int, MutableSet<String>> {
|
): Pair<Int, MutableSet<ResultPhotosFiles>> = withContext(Dispatchers.Default) {
|
||||||
val currentSelected = mutableSetOf<String>()
|
val currentSelected = mutableSetOf<ResultPhotosFiles>()
|
||||||
val totalSelectedCount = list.count {
|
var totalSelectedCount = 0
|
||||||
val isSelected = it.path in selected
|
|
||||||
if (isSelected) currentSelected.add(it.path.toString())
|
// 高效遍历外层 + 内层列表
|
||||||
isSelected
|
for (pair in list) {
|
||||||
|
if (pair in selected) {
|
||||||
|
totalSelectedCount++
|
||||||
|
currentSelected.add(pair)
|
||||||
}
|
}
|
||||||
showLog("-------totalSelectedCount 222=${totalSelectedCount}")
|
|
||||||
return totalSelectedCount to currentSelected
|
|
||||||
}
|
}
|
||||||
|
totalSelectedCount to currentSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去掉缩略图的集合
|
||||||
|
*/
|
||||||
|
suspend fun filterThumbnailsAsync(
|
||||||
|
originalList: MutableList<Pair<String, List<ResultPhotosFiles>>>
|
||||||
|
): List<Pair<String, List<ResultPhotosFiles>>> = withContext(Dispatchers.Default) {
|
||||||
|
originalList.asSequence()
|
||||||
|
.map { (key, files) ->
|
||||||
|
key to files.asSequence().filter { !it.isThumbnail }.toList()
|
||||||
|
}
|
||||||
|
.filter { it.second.isNotEmpty() }
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getFormatDate(time: Long): String {
|
fun getFormatDate(time: Long): String {
|
||||||
return dateFormat.format(Date(time))
|
return dateFormat.format(Date(time))
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import android.os.Parcelable
|
|||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
|
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
object ExtendFunctions {
|
object ExtendFunctions {
|
||||||
@ -176,7 +178,31 @@ object ExtendFunctions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun Int.mbToBytes(): Long {
|
fun Int.mbToBytes(): Long {
|
||||||
return this * 1024L * 1024L
|
return this * 1000L * 1000L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除掉缩略图后的数据
|
||||||
|
*/
|
||||||
|
suspend fun List<Pair<String, List<ResultPhotosFiles>>>.filterThumbnailsAsync(): List<Pair<String, List<ResultPhotosFiles>>> =
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
this@filterThumbnailsAsync.asSequence()
|
||||||
|
.mapNotNull { (key, files) ->
|
||||||
|
val filtered = files.asSequence().filter { !it.isThumbnail }.toList()
|
||||||
|
if (filtered.isNotEmpty()) key to filtered else null
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除掉缩略图后的数据
|
||||||
|
*/
|
||||||
|
suspend fun List<ResultPhotosFiles>.filterRemoveThumbnailsAsync(): List<ResultPhotosFiles> =
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
this@filterRemoveThumbnailsAsync.asSequence()
|
||||||
|
.filter { !it.isThumbnail } // 去掉 isThumbnail = true 的项
|
||||||
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
package com.ux.video.file.filerecovery.utils
|
package com.ux.video.file.filerecovery.utils
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.OpenableColumns
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import com.ux.video.file.filerecovery.photo.ResultPhotos
|
import com.ux.video.file.filerecovery.photo.ResultPhotos
|
||||||
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
|
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
|
||||||
import com.ux.video.file.filerecovery.result.ScanningActivity
|
import com.ux.video.file.filerecovery.result.ScanningActivity
|
||||||
@ -48,6 +54,7 @@ object ScanManager {
|
|||||||
* 扫描所有文件
|
* 扫描所有文件
|
||||||
*/
|
*/
|
||||||
fun scanAllDocuments(
|
fun scanAllDocuments(
|
||||||
|
context: Context,
|
||||||
root: File, maxDepth: Int = 5,
|
root: File, maxDepth: Int = 5,
|
||||||
maxFiles: Int = 5000,
|
maxFiles: Int = 5000,
|
||||||
type: Int
|
type: Int
|
||||||
@ -100,6 +107,7 @@ object ScanManager {
|
|||||||
name = file.name,
|
name = file.name,
|
||||||
path = file.absolutePath,
|
path = file.absolutePath,
|
||||||
size = file.length(),
|
size = file.length(),
|
||||||
|
sizeString = android.text.format.Formatter.formatFileSize(context, file.length()),
|
||||||
lastModified = file.lastModified(),
|
lastModified = file.lastModified(),
|
||||||
resolution = getImageSize(file).run {
|
resolution = getImageSize(file).run {
|
||||||
"$first*$second"
|
"$first*$second"
|
||||||
@ -110,6 +118,7 @@ object ScanManager {
|
|||||||
}
|
}
|
||||||
emit(ScanState.Complete(ArrayList(map)))
|
emit(ScanState.Complete(ArrayList(map)))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getImageSize(file: File): Pair<Int, Int> {
|
private fun getImageSize(file: File): Pair<Int, Int> {
|
||||||
val options = BitmapFactory.Options()
|
val options = BitmapFactory.Options()
|
||||||
options.inJustDecodeBounds = true
|
options.inJustDecodeBounds = true
|
||||||
@ -125,12 +134,14 @@ object ScanManager {
|
|||||||
* @param maxFiles // 最大收集的文件数
|
* @param maxFiles // 最大收集的文件数
|
||||||
*/
|
*/
|
||||||
fun scanHiddenPhotoAsync(
|
fun scanHiddenPhotoAsync(
|
||||||
|
context: Context,
|
||||||
root: File, maxDepth: Int = 5,
|
root: File, maxDepth: Int = 5,
|
||||||
maxFiles: Int = 5000, type: Int
|
maxFiles: Int = 5000, type: Int
|
||||||
): Flow<ScanState> = flow {
|
): Flow<ScanState> = flow {
|
||||||
|
|
||||||
val result = mutableMapOf<String, MutableList<File>>()
|
val result = mutableMapOf<String, MutableList<File>>()
|
||||||
var fileCount = 0
|
var fileCount = 0
|
||||||
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
suspend fun scanDir(dir: File, depth: Int, insideHidden: Boolean = false) {
|
suspend fun scanDir(dir: File, depth: Int, insideHidden: Boolean = false) {
|
||||||
if (!dir.exists() || !dir.isDirectory) return
|
if (!dir.exists() || !dir.isDirectory) return
|
||||||
if (depth > maxDepth || fileCount >= maxFiles) return
|
if (depth > maxDepth || fileCount >= maxFiles) return
|
||||||
@ -181,17 +192,30 @@ object ScanManager {
|
|||||||
name = file.name,
|
name = file.name,
|
||||||
path = file.absolutePath,
|
path = file.absolutePath,
|
||||||
size = file.length(),
|
size = file.length(),
|
||||||
|
sizeString = android.text.format.Formatter.formatFileSize(context, file.length()),
|
||||||
lastModified = file.lastModified(),
|
lastModified = file.lastModified(),
|
||||||
resolution = getImageSize(file).run {
|
resolution = getImageSize(file).run {
|
||||||
"$first*$second"
|
"$first*$second"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
ResultPhotos(dir, ArrayList(resultPhotosFilesList))
|
ResultPhotos(dir, ArrayList(resultPhotosFilesList))
|
||||||
}
|
}
|
||||||
emit(ScanState.Complete(ArrayList(map)))
|
emit(ScanState.Complete(ArrayList(map)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getFileSizeByMediaStore(context: Context, file: File): Long {
|
||||||
|
val uri = Uri.fromFile(file)
|
||||||
|
context.contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null)
|
||||||
|
?.use { cursor ->
|
||||||
|
val index = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
return cursor.getLong(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file.length() // fallback
|
||||||
|
}
|
||||||
|
|
||||||
private fun isFormatFile(file: File, types: List<String>): Boolean {
|
private fun isFormatFile(file: File, types: List<String>): Boolean {
|
||||||
val ext = file.extension.lowercase()
|
val ext = file.extension.lowercase()
|
||||||
@ -223,7 +247,7 @@ object ScanManager {
|
|||||||
* @param folder "AllRecovery/Photo"
|
* @param folder "AllRecovery/Photo"
|
||||||
*/
|
*/
|
||||||
fun CoroutineScope.copySelectedFilesAsync(
|
fun CoroutineScope.copySelectedFilesAsync(
|
||||||
selectedSet: Set<String>,
|
selectedSet: Set<ResultPhotosFiles>,
|
||||||
rootDir: File = Common.rootDir,
|
rootDir: File = Common.rootDir,
|
||||||
folder: String,
|
folder: String,
|
||||||
onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit,
|
onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit,
|
||||||
@ -232,8 +256,8 @@ object ScanManager {
|
|||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
val targetDir = File(rootDir, folder)
|
val targetDir = File(rootDir, folder)
|
||||||
if (!targetDir.exists()) targetDir.mkdirs()
|
if (!targetDir.exists()) targetDir.mkdirs()
|
||||||
selectedSet.forEachIndexed { index, path ->
|
selectedSet.forEachIndexed { index, resultPhotosFiles ->
|
||||||
val srcFile = File(path)
|
val srcFile = File(resultPhotosFiles.path!!)
|
||||||
if (srcFile.exists() && srcFile.isFile) {
|
if (srcFile.exists() && srcFile.isFile) {
|
||||||
val destFile = File(targetDir, srcFile.name)
|
val destFile = File(targetDir, srcFile.name)
|
||||||
var success = false
|
var success = false
|
||||||
@ -265,23 +289,23 @@ object ScanManager {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun CoroutineScope.deleteFilesAsync(
|
fun CoroutineScope.deleteFilesAsync(
|
||||||
selectedSet: Set<String>,
|
selectedSet: Set<ResultPhotosFiles>,
|
||||||
onProgress: (currentCounts: Int, path: String, success: Boolean) -> Unit,
|
onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit,
|
||||||
onComplete: (currentCounts: Int) -> Unit
|
onComplete: (currentCounts: Int) -> Unit
|
||||||
) {
|
) {
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
var deletedCount = 0
|
var deletedCount = 0
|
||||||
selectedSet.forEachIndexed { index, path ->
|
selectedSet.forEachIndexed { index, resultPhotosFiles ->
|
||||||
try {
|
try {
|
||||||
val file = File(path)
|
val file = File(resultPhotosFiles.path!!)
|
||||||
if (file.exists() && file.delete()) {
|
if (file.exists() && file.delete()) {
|
||||||
deletedCount++
|
deletedCount++
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
onProgress(index + 1, path, true)
|
onProgress(index + 1, file.name, true)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
onProgress(index + 1, path, false)
|
onProgress(index + 1, resultPhotosFiles.path!!, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.ux.video.file.filerecovery.photo.ResultPhotos
|
import com.ux.video.file.filerecovery.photo.ResultPhotos
|
||||||
|
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
@ -19,24 +20,27 @@ class ScanRepository : ViewModel() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private val _selectedLiveData = MutableLiveData<Set<String>>(emptySet())
|
private val _selectedLiveData = MutableLiveData<Set<ResultPhotosFiles>>(emptySet())
|
||||||
val selectedLiveData: LiveData<Set<String>> = _selectedLiveData
|
val selectedLiveData: LiveData<Set<ResultPhotosFiles>> = _selectedLiveData
|
||||||
|
|
||||||
// 当前筛选显示的选中项
|
// 当前筛选显示的选中项
|
||||||
private val _selectedDisplayLiveData = MutableLiveData<Set<String>>(emptySet())
|
private val _selectedDisplayLiveData = MutableLiveData<Set<ResultPhotosFiles>>(emptySet())
|
||||||
val selectedDisplayLiveData: LiveData<Set<String>> = _selectedDisplayLiveData
|
val selectedDisplayLiveData: LiveData<Set<ResultPhotosFiles>> = _selectedDisplayLiveData
|
||||||
|
|
||||||
fun toggleSelection(isAdd: Boolean, path: String) {
|
fun toggleSelection(isAdd: Boolean, resultPhotosFiles: ResultPhotosFiles) {
|
||||||
val current = _selectedLiveData.value?.toMutableSet() ?: mutableSetOf()
|
val current = _selectedLiveData.value?.toMutableSet() ?: mutableSetOf()
|
||||||
val currentDisplay = _selectedDisplayLiveData.value?.toMutableSet() ?: mutableSetOf()
|
val currentDisplay = _selectedDisplayLiveData.value?.toMutableSet() ?: mutableSetOf()
|
||||||
|
resultPhotosFiles.let {
|
||||||
|
|
||||||
if (isAdd) {
|
if (isAdd) {
|
||||||
current.add(path)
|
current.add(it)
|
||||||
currentDisplay.add(path)
|
currentDisplay.add(it)
|
||||||
} else {
|
} else {
|
||||||
current.remove(path)
|
current.remove(it)
|
||||||
currentDisplay.remove(path)
|
currentDisplay.remove(it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Common.showLog( "toggleSelection------------ _selectedDisplayFlow=${_selectedDisplayLiveData.value?.size} _selectedFlow=${_selectedLiveData.value?.size} ")
|
Common.showLog( "toggleSelection------------ _selectedDisplayFlow=${_selectedDisplayLiveData.value?.size} _selectedFlow=${_selectedLiveData.value?.size} ")
|
||||||
_selectedLiveData.value = current.toSet()
|
_selectedLiveData.value = current.toSet()
|
||||||
_selectedDisplayLiveData.value = currentDisplay.toSet()
|
_selectedDisplayLiveData.value = currentDisplay.toSet()
|
||||||
@ -47,9 +51,9 @@ class ScanRepository : ViewModel() {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据筛选后重置当前显示的选中集合
|
* 数据筛选后或者缩略图显示开关切换后 重置当前显示的选中集合
|
||||||
*/
|
*/
|
||||||
fun filterResetDisplayFlow(list: MutableSet<String>){
|
fun filterResetDisplayFlow(list: MutableSet<ResultPhotosFiles>){
|
||||||
_selectedDisplayLiveData.value = list.toSet()
|
_selectedDisplayLiveData.value = list.toSet()
|
||||||
|
|
||||||
Common.showLog( "筛选后重置 _selectedDisplayFlow=${_selectedDisplayLiveData.value?.size} _selectedFlow=${_selectedLiveData.value?.size} ")
|
Common.showLog( "筛选后重置 _selectedDisplayFlow=${_selectedDisplayLiveData.value?.size} _selectedFlow=${_selectedLiveData.value?.size} ")
|
||||||
@ -57,13 +61,10 @@ class ScanRepository : ViewModel() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun checkIsSelect(path: String): Boolean {
|
fun checkIsSelect(resultPhotosFiles: ResultPhotosFiles): Boolean {
|
||||||
val current = _selectedDisplayLiveData.value
|
val current = _selectedLiveData.value
|
||||||
return current?.contains(path) == true
|
return current?.contains(resultPhotosFiles) == true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val instance by lazy { ScanRepository() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="16dp"
|
android:width="28dp"
|
||||||
android:height="16dp"
|
android:height="28dp"
|
||||||
android:viewportWidth="1024"
|
android:viewportWidth="1024"
|
||||||
android:viewportHeight="1024">
|
android:viewportHeight="1024">
|
||||||
<path
|
<path
|
||||||
android:pathData="M518,87.1c-235.3,0 -426,190.7 -426,426s190.7,426 426,426 426,-190.7 426,-426 -190.7,-426 -426,-426zM518,855.1c-188.9,0 -342.1,-153.2 -342.1,-342.1S329,170.9 518,170.9s342.1,153.2 342.1,342.1 -153.2,342.1 -342.1,342.1z"
|
android:pathData="M518,87.1c-235.3,0 -426,190.7 -426,426s190.7,426 426,426 426,-190.7 426,-426 -190.7,-426 -426,-426zM518,855.1c-188.9,0 -342.1,-153.2 -342.1,-342.1S329,170.9 518,170.9s342.1,153.2 342.1,342.1 -153.2,342.1 -342.1,342.1z"
|
||||||
android:fillColor="@color/white"/>
|
android:fillColor="@color/black"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@ -55,6 +55,10 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/date"
|
android:text="@string/date"
|
||||||
|
android:maxWidth="85dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:id="@+id/title_date"
|
||||||
android:textColor="@color/selector_black_blue"
|
android:textColor="@color/selector_black_blue"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:fontType="bold" />
|
app:fontType="bold" />
|
||||||
@ -80,6 +84,10 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/size"
|
android:text="@string/size"
|
||||||
|
android:maxWidth="85dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:id="@+id/title_size"
|
||||||
|
android:ellipsize="end"
|
||||||
android:textColor="@color/selector_black_blue"
|
android:textColor="@color/selector_black_blue"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:fontType="bold" />
|
app:fontType="bold" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user