增加数据库

This commit is contained in:
litingting 2025-10-29 09:55:30 +08:00
parent 56f3f48dd9
commit b62abaec37
53 changed files with 1003 additions and 295 deletions

View File

@ -3,6 +3,7 @@ plugins {
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
id ("kotlin-kapt") id ("kotlin-kapt")
id ("kotlin-parcelize") id ("kotlin-parcelize")
id("io.objectbox")
} }
android { android {
@ -47,6 +48,9 @@ dependencies {
implementation(libs.material) implementation(libs.material)
implementation(libs.androidx.activity) implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout) implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.lifecycle.livedata.ktx)
implementation(libs.androidx.lifecycle.viewmodel.ktx)
implementation(libs.androidx.fragment.ktx)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)

View File

@ -0,0 +1,78 @@
{
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "2:1219714858038066790",
"lastPropertyId": "8:5134665607056785092",
"name": "ResultDataFiles",
"properties": [
{
"id": "1:1863268749092884504",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:685886748782553244",
"name": "fileType",
"type": 5
},
{
"id": "3:313976899017664946",
"name": "name",
"type": 9
},
{
"id": "4:3491625332486824618",
"name": "path",
"type": 9
},
{
"id": "5:2788384583815433725",
"name": "size",
"type": 6
},
{
"id": "6:8902434027574194684",
"name": "sizeString",
"type": 9
},
{
"id": "7:8305655849151310709",
"name": "lastModified",
"type": 6
},
{
"id": "8:5134665607056785092",
"name": "resolution",
"type": 9
}
],
"relations": []
}
],
"lastEntityId": "2:1219714858038066790",
"lastIndexId": "0:0",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 5,
"modelVersionParserMinimum": 5,
"retiredEntityUids": [
4830032338777965154
],
"retiredIndexUids": [],
"retiredPropertyUids": [
7967846713481917737,
5916406351352231902,
5265574492754659377,
1284016652408596993,
3831579248666795267,
4445203567182319472,
6858261029979256233,
2835789057594891780
],
"retiredRelationUids": [],
"version": 1
}

View File

@ -2,12 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<!-- Android 10 及以下 --> <!-- Android 10 及以下 -->
<uses-permission <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
android:name="android.permission.READ_EXTERNAL_STORAGE" <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Android 11+ -->
android:maxSdkVersion="28" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" /> <!-- Android 11+ -->
<uses-permission <uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
@ -24,14 +20,17 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.FileRecovery" android:theme="@style/Theme.FileRecovery"
tools:targetApi="31"> tools:targetApi="31">
<activity
android:name=".recovery.RecoveryActivity"
android:exported="false" />
<activity <activity
android:name=".video.VideoPlayActivity" android:name=".video.VideoPlayActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".photo.PhotoInfoActivity" android:name=".sort.PhotoInfoActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".photo.PhotoSortingActivity" android:name=".sort.PhotoSortingActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".result.ScanResultDisplayActivity" android:name=".result.ScanResultDisplayActivity"

View File

@ -1,6 +1,7 @@
package com.ux.video.file.filerecovery package com.ux.video.file.filerecovery
import android.app.Application import android.app.Application
import com.ux.video.file.filerecovery.db.ObjectBoxManager
import org.jaaksi.pickerview.widget.BasePickerView import org.jaaksi.pickerview.widget.BasePickerView
class App: Application() { class App: Application() {
@ -14,5 +15,6 @@ class App: Application() {
BasePickerView.sDefaultItemSize = 40 BasePickerView.sDefaultItemSize = 40
// BasePickerView.sDefaultDrawIndicator = false // BasePickerView.sDefaultDrawIndicator = false
ObjectBoxManager.initObjectBoxDb(this)
} }
} }

View File

@ -0,0 +1,24 @@
package com.ux.video.file.filerecovery.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
abstract class BaseFragment<T : ViewBinding> : Fragment() {
protected lateinit var binding: T
abstract fun initBinding(inflater: LayoutInflater, container: ViewGroup?): T
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = initBinding(inflater,container)
return binding.root
}
}

View File

@ -0,0 +1,144 @@
package com.ux.video.file.filerecovery.db
import android.content.Context
import com.ux.video.file.filerecovery.utils.Common
import io.objectbox.Box
import io.objectbox.BoxStore
import io.objectbox.config.DebugFlags
import io.objectbox.reactive.DataSubscription
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.jvm.java
object ObjectBoxManager {
val pageSize = 10
lateinit var objectboxStore: BoxStore
var AllRecoveryBox: Box<ResultDataFiles>? = null
val observer: DataSubscription? = null
fun initObjectBoxDb(context: Context) {
objectboxStore = MyObjectBox.builder().androidContext(context)
.debugFlags(DebugFlags.LOG_QUERIES or DebugFlags.LOG_QUERY_PARAMETERS)
.build()
}
private fun getDataBox(): Box<ResultDataFiles> {
AllRecoveryBox =
AllRecoveryBox ?: objectboxStore.boxFor<ResultDataFiles>(ResultDataFiles::class.java)
return AllRecoveryBox!!
}
// fun getLikeBox(): Box<ResultPhotosFiles>? {
// likeWallpaperBox = likeWallpaperBox ?: boxStore?.boxFor<LikeWallpaper>(ResultPhotosFiles::class.java)
// return likeWallpaperBox
// }
//
// fun setLikeUpdateListener(queryAllLike:(List<LikeWallpaper>)->Unit): DataSubscription {
// val likeBox: Box<LikeWallpaper>? = getLikeBox()
// val build: Query<LikeWallpaper> = likeBox!!.query()
// .build()
// return build.subscribe(DataSubscriptionList())
// .on(AndroidScheduler.mainThread())
// .observer { data ->
// Helper.showMyLog("---OnLikeUpdateListener-------------" + data.size)
// queryAllLike(data)
// }
// }
//
fun addRecoveryFile(resultData: ResultDataFiles) {
val objectBox: Box<ResultDataFiles> = getDataBox()
val first = objectBox.query(ResultDataFiles_.path.equal(resultData.path))
.build().findFirst()
if (first == null) {
objectBox.put(resultData)
Common.showLog("----------addRecoveryFile----${resultData.path}")
}
}
//
//
// fun insertLike(favoriteData: LikeWallpaper) {
// val likeBox: Box<LikeWallpaper>? = getLikeBox()
// val first: LikeWallpaper? = likeBox!!.query()
// .equal(LikeWallpaper_.id, favoriteData.id)
// .build()
// .findFirst()
// if (first == null) {
// Helper.showMyLog("---insertLike-----------------id=" + favoriteData.id)
// likeBox.put(favoriteData)
// } else {
// Helper.showMyLog("---insertLike-----------------id=" + favoriteData.id)
// }
// }
//
//
fun deleteRecoveryFile(resultData: ResultDataFiles) {
val objectBox = getDataBox()
val first =
objectBox.query(ResultDataFiles_.path.equal(resultData.path)).build().findFirst()
if (first != null) {
val remove = objectBox.remove(first.id)
Common.showLog("----------addRecoveryFile---删除${remove}-${resultData.path}")
}
}
//
// fun queryIsLike(id: Long): Boolean {
// val likeBox: Box<LikeWallpaper>? = getLikeBox()
// val first: LikeWallpaper? = likeBox!!.query()
// .equal(LikeWallpaper_.id, id)
// .build()
// .findFirst()
// return first != null
// }
//
//
//
fun queryRecoveryFile(type: Int): List<ResultDataFiles> {
val dataBox = getDataBox()
val list: List<ResultDataFiles> = dataBox.query(ResultDataFiles_.fileType.equal(type))
.build()
.find()
return list
}
suspend fun queryRecoveryFileAsync(type: Int): List<ResultDataFiles> =
withContext(Dispatchers.IO) {
val dataBox = getDataBox()
dataBox.query(ResultDataFiles_.fileType.equal(type))
.build()
.find()
}
//
// fun queryLike(currentPage: Int): List<LikeWallpaper> {
// val offset: Int = (currentPage - 1) * pageSize
// val likeBox: Box<LikeWallpaper>? = getLikeBox()
// val data: List<LikeWallpaper> = likeBox!!.query()
// .build()
// .find(offset.toLong(), pageSize.toLong())
//
// return data
// }
//
// fun queryAllData(wallpaperType: Long): List<VideoWallpaper> {
// val objectBoxLike: Box<VideoWallpaper>? = getDataBox()
// val data: List<VideoWallpaper> = objectBoxLike!!.query()
// .equal(VideoWallpaper_.wallpapertype, wallpaperType)
// .build()
// .find()
//
// return data
// }
}

View File

@ -1,11 +1,11 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.db
import android.os.Parcelable import android.os.Parcelable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class ResultPhotos( data class ResultData(
val dirName: String, val dirName: String,
val allFiles: ArrayList<ResultPhotosFiles> val allFiles: ArrayList<ResultDataFiles>
): Parcelable ): Parcelable

View File

@ -1,15 +1,22 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.db
import java.io.File import java.io.File
import android.os.Parcelable import android.os.Parcelable
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Entity
@Parcelize @Parcelize
data class ResultPhotosFiles( data class ResultDataFiles(
@Id
var id: Long = 0,
// 0-3 photo\video\audio\documents
var fileType: Int,
val name: String, val name: String,
val path: String? = null, val path: String,
val size: Long, // 字节 val size: Long, // 字节
val sizeString: String, val sizeString: String,
val lastModified: Long, // 时间戳 val lastModified: Long, // 时间戳
@ -36,4 +43,5 @@ data class ResultPhotosFiles(
val duration: Long val duration: Long
get() { get() {
return Common.getMediaDuration(path.toString()) } return Common.getMediaDuration(path.toString()) }
} }

View File

@ -5,10 +5,10 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import com.ux.video.file.filerecovery.base.BaseAdapter import com.ux.video.file.filerecovery.base.BaseAdapter
import com.ux.video.file.filerecovery.databinding.DocumentsScanResultAdapterBinding import com.ux.video.file.filerecovery.databinding.DocumentsScanResultAdapterBinding
import com.ux.video.file.filerecovery.photo.ResultPhotos import com.ux.video.file.filerecovery.db.ResultData
class DocumentsScanResultAdapter(mContext: Context) : class DocumentsScanResultAdapter(mContext: Context) :
BaseAdapter<ResultPhotos, DocumentsScanResultAdapterBinding>(mContext) { BaseAdapter<ResultData, DocumentsScanResultAdapterBinding>(mContext) {
override fun getViewBinding(parent: ViewGroup): DocumentsScanResultAdapterBinding = override fun getViewBinding(parent: ViewGroup): DocumentsScanResultAdapterBinding =
DocumentsScanResultAdapterBinding.inflate( DocumentsScanResultAdapterBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -18,7 +18,7 @@ class DocumentsScanResultAdapter(mContext: Context) :
override fun bindItem( override fun bindItem(
holder: VHolder<DocumentsScanResultAdapterBinding>, holder: VHolder<DocumentsScanResultAdapterBinding>,
item: ResultPhotos item: ResultData
) { ) {
holder.vb.run { holder.vb.run {

View File

@ -1,7 +1,6 @@
package com.ux.video.file.filerecovery.main package com.ux.video.file.filerecovery.main
import android.Manifest import android.Manifest
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -20,9 +19,8 @@ import androidx.core.view.isVisible
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityMainBinding import com.ux.video.file.filerecovery.databinding.ActivityMainBinding
import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity import com.ux.video.file.filerecovery.recovery.RecoveryActivity
import com.ux.video.file.filerecovery.photo.DateFilterPopupWindows import com.ux.video.file.filerecovery.sort.DatePickerDialogFragment
import com.ux.video.file.filerecovery.photo.DatePickerDialogFragment
import com.ux.video.file.filerecovery.utils.ScanManager import com.ux.video.file.filerecovery.utils.ScanManager
class MainActivity : BaseActivity<ActivityMainBinding>() { class MainActivity : BaseActivity<ActivityMainBinding>() {
@ -95,6 +93,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
currentGoType = ScanSelectTypeActivity.Companion.VALUE_DOCUMENT currentGoType = ScanSelectTypeActivity.Companion.VALUE_DOCUMENT
intentCheck() intentCheck()
} }
layoutRecovery.setOnClickListener {
startActivity(Intent(this@MainActivity,RecoveryActivity::class.java))
}
} }
binding.tvTitle.setOnClickListener { binding.tvTitle.setOnClickListener {

View File

@ -0,0 +1,74 @@
package com.ux.video.file.filerecovery.recovery
import android.view.LayoutInflater
import androidx.lifecycle.lifecycleScope
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.google.android.material.tabs.TabLayoutMediator
import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityRecoveryBinding
import com.ux.video.file.filerecovery.db.ObjectBoxManager
import com.ux.video.file.filerecovery.recovery.ui.MyViewPage2Adapter
import com.ux.video.file.filerecovery.recovery.ui.recoveryphoto.RecoveryPhotoFragment
import com.ux.video.file.filerecovery.utils.CustomTextView
import kotlinx.coroutines.launch
class RecoveryActivity : BaseActivity<ActivityRecoveryBinding>() {
override fun inflateBinding(inflater: LayoutInflater): ActivityRecoveryBinding = ActivityRecoveryBinding.inflate(inflater)
override fun initView() {
super.initView()
binding.run {
viewPage2.adapter = MyViewPage2Adapter(this@RecoveryActivity,listOf(RecoveryPhotoFragment(),RecoveryPhotoFragment(),RecoveryPhotoFragment(),RecoveryPhotoFragment()))
TabLayoutMediator(tabLayout,viewPage2){tab,position->
val tabView = LayoutInflater.from(this@RecoveryActivity).inflate(R.layout.tab_layout_item, null)
val tvTitle = tabView.findViewById<CustomTextView>(R.id.tab_item_title)
val tvCount = tabView.findViewById<CustomTextView>(R.id.tab_item_count)
tvCount.text = getString(R.string.text_counts, 0)
when(position){
0->{tvTitle.text = getString(R.string.text_photos)}
1->{tvTitle.text = getString(R.string.text_videos)}
2->{tvTitle.text = getString(R.string.text_audios)}
3->{tvTitle.text = getString(R.string.text_documents)}
}
tab.customView = tabView
}.attach()
tabLayout.addOnTabSelectedListener(object :OnTabSelectedListener{
override fun onTabSelected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
})
}
}
override fun initData() {
super.initData()
lifecycleScope.launch{
val recoveryFilePhoto = ObjectBoxManager.queryRecoveryFileAsync(0)
val recoveryFileVideo = ObjectBoxManager.queryRecoveryFileAsync(1)
val recoveryFileAudio = ObjectBoxManager.queryRecoveryFileAsync(2)
val recoveryFileDocuments = ObjectBoxManager.queryRecoveryFileAsync(3)
binding.tabLayout.run {
getTabAt(0)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text = getString(R.string.text_counts,recoveryFilePhoto.size)
getTabAt(1)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text = getString(R.string.text_counts,recoveryFileVideo.size)
getTabAt(2)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text = getString(R.string.text_counts,recoveryFileAudio.size)
getTabAt(3)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text = getString(R.string.text_counts,recoveryFileDocuments.size)
}
}
}
}

View File

@ -0,0 +1,14 @@
package com.ux.video.file.filerecovery.recovery.ui
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
class MyViewPage2Adapter(
fragmentActivity: FragmentActivity,
private val fragments: List<Fragment>
) : FragmentStateAdapter(fragmentActivity) {
override fun getItemCount(): Int = fragments.size
override fun createFragment(position: Int): Fragment = fragments[position]
}

View File

@ -0,0 +1,32 @@
package com.ux.video.file.filerecovery.recovery.ui.recoveryphoto
import androidx.fragment.app.viewModels
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseFragment
import com.ux.video.file.filerecovery.databinding.FragmentRecoveryPhotoBinding
class RecoveryPhotoFragment : BaseFragment<FragmentRecoveryPhotoBinding>() {
companion object {
fun newInstance() = RecoveryPhotoFragment()
}
private val viewModel: RecoveryPhotoViewModel by viewModels()
override fun initBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRecoveryPhotoBinding =
FragmentRecoveryPhotoBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}

View File

@ -0,0 +1,7 @@
package com.ux.video.file.filerecovery.recovery.ui.recoveryphoto
import androidx.lifecycle.ViewModel
class RecoveryPhotoViewModel : ViewModel() {
}

View File

@ -7,9 +7,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityScanResultDisplayBinding import com.ux.video.file.filerecovery.databinding.ActivityScanResultDisplayBinding
import com.ux.video.file.filerecovery.photo.PhotoSortingActivity import com.ux.video.file.filerecovery.sort.PhotoSortingActivity
import com.ux.video.file.filerecovery.photo.ResultPhotos import com.ux.video.file.filerecovery.db.ResultData
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio
@ -28,7 +28,7 @@ class ScanResultDisplayActivity : BaseActivity<ActivityScanResultDisplayBinding>
private var scanType: Int = VALUE_SCAN_TYPE_photo private var scanType: Int = VALUE_SCAN_TYPE_photo
private var exitDialog: ExitDialogFragment? = null private var exitDialog: ExitDialogFragment? = null
private var list: ArrayList<ResultPhotos>? = null private var list: ArrayList<ResultData>? = null
companion object { companion object {
val KEY_SCAN_RESULT = "scan_result" val KEY_SCAN_RESULT = "scan_result"
@ -136,7 +136,7 @@ class ScanResultDisplayActivity : BaseActivity<ActivityScanResultDisplayBinding>
} }
private fun goSort(list: ArrayList<ResultPhotosFiles>) { private fun goSort(list: ArrayList<ResultDataFiles>) {
startActivity( startActivity(
Intent( Intent(
this@ScanResultDisplayActivity, this@ScanResultDisplayActivity,

View File

@ -4,20 +4,12 @@ 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 android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseAdapter import com.ux.video.file.filerecovery.base.BaseAdapter
import com.ux.video.file.filerecovery.databinding.ScanResultAdapterBinding
import com.ux.video.file.filerecovery.databinding.ScanResultDocumentsAdapterBinding import com.ux.video.file.filerecovery.databinding.ScanResultDocumentsAdapterBinding
import com.ux.video.file.filerecovery.photo.ResultPhotos import com.ux.video.file.filerecovery.db.ResultData
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
import java.io.File
/** /**
* 文件或者音频的扫描结果汇总适配器 * 文件或者音频的扫描结果汇总适配器
@ -25,9 +17,9 @@ import java.io.File
class ScanResultDocumentsAdapter( class ScanResultDocumentsAdapter(
mContext: Context, mContext: Context,
var type: Int, var type: Int,
var onClickItem: (allFiles: ArrayList<ResultPhotosFiles>) -> Unit var onClickItem: (allFiles: ArrayList<ResultDataFiles>) -> Unit
) : ) :
BaseAdapter<ResultPhotos, ScanResultDocumentsAdapterBinding>(mContext) { BaseAdapter<ResultData, ScanResultDocumentsAdapterBinding>(mContext) {
override fun getViewBinding(parent: ViewGroup): ScanResultDocumentsAdapterBinding = override fun getViewBinding(parent: ViewGroup): ScanResultDocumentsAdapterBinding =
ScanResultDocumentsAdapterBinding.inflate( ScanResultDocumentsAdapterBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -38,7 +30,7 @@ class ScanResultDocumentsAdapter(
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun bindItem( override fun bindItem(
holder: VHolder<ScanResultDocumentsAdapterBinding>, holder: VHolder<ScanResultDocumentsAdapterBinding>,
item: ResultPhotos item: ResultData
) { ) {
holder.vb.run { holder.vb.run {

View File

@ -11,17 +11,17 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.ux.video.file.filerecovery.base.BaseAdapter import com.ux.video.file.filerecovery.base.BaseAdapter
import com.ux.video.file.filerecovery.databinding.ScanResultAdapterBinding import com.ux.video.file.filerecovery.databinding.ScanResultAdapterBinding
import com.ux.video.file.filerecovery.photo.ResultPhotos import com.ux.video.file.filerecovery.db.ResultData
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
import java.io.File import java.io.File
class ScanResultPhotoAdapter( class ScanResultPhotoAdapter(
mContext: Context, mContext: Context,
var type: Int, var type: Int,
var onClickItem: (allFiles: ArrayList<ResultPhotosFiles>) -> Unit var onClickItem: (allFiles: ArrayList<ResultDataFiles>) -> Unit
) : ) :
BaseAdapter<ResultPhotos, ScanResultAdapterBinding>(mContext) { BaseAdapter<ResultData, ScanResultAdapterBinding>(mContext) {
override fun getViewBinding(parent: ViewGroup): ScanResultAdapterBinding = override fun getViewBinding(parent: ViewGroup): ScanResultAdapterBinding =
ScanResultAdapterBinding.inflate( ScanResultAdapterBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@ -32,7 +32,7 @@ class ScanResultPhotoAdapter(
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun bindItem( override fun bindItem(
holder: VHolder<ScanResultAdapterBinding>, holder: VHolder<ScanResultAdapterBinding>,
item: ResultPhotos item: ResultData
) { ) {
holder.vb.run { holder.vb.run {

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.content.Context import android.content.Context

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseIngDialogFragment import com.ux.video.file.filerecovery.base.BaseIngDialogFragment

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
@ -9,7 +9,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.PopupWindow import android.widget.PopupWindow
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.view.forEach
import com.ux.video.file.filerecovery.databinding.CommonLayoutFilterItemBinding import com.ux.video.file.filerecovery.databinding.CommonLayoutFilterItemBinding
import com.ux.video.file.filerecovery.databinding.PopwindowsFilterBinding import com.ux.video.file.filerecovery.databinding.PopwindowsFilterBinding
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common

View File

@ -1,18 +1,18 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.annotation.SuppressLint 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.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
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.db.ResultDataFiles
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.Common.VALUE_SCAN_TYPE_deleted_documents
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents
import com.ux.video.file.filerecovery.utils.ScanRepository import com.ux.video.file.filerecovery.utils.ScanRepository
class PhotoDisplayDateAdapter( class PhotoDisplayDateAdapter(
@ -20,10 +20,10 @@ class PhotoDisplayDateAdapter(
var scanType: Int, var scanType: Int,
var mColumns: Int, var mColumns: Int,
var viewModel: ScanRepository, var viewModel: ScanRepository,
var onSelectedUpdate: (resultPhotosFiles: ResultPhotosFiles, isAdd: Boolean) -> Unit, var onSelectedUpdate: (resultDataFiles: ResultDataFiles, isAdd: Boolean) -> Unit,
var clickItem: (item: ResultPhotosFiles) -> Unit var clickItem: (item: ResultDataFiles) -> Unit
) : ) :
BaseAdapter<Pair<String, List<ResultPhotosFiles>>, PhotoDisplayDateAdapterBinding>(mContext) { BaseAdapter<Pair<String, List<ResultDataFiles>>, PhotoDisplayDateAdapterBinding>(mContext) {
private var allSelected: Boolean? = null private var allSelected: Boolean? = null
@ -75,7 +75,7 @@ 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<ResultDataFiles>>
) { ) {
holder.vb.run { holder.vb.run {
item.run { item.run {
@ -109,9 +109,10 @@ class PhotoDisplayDateAdapter(
recyclerChild.apply { recyclerChild.apply {
layoutManager = when (scanType) { layoutManager = when (scanType) {
Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio -> { Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> {
LinearLayoutManager(context) LinearLayoutManager(context)
} }
else -> { else -> {
GridLayoutManager(context, mColumns) GridLayoutManager(context, mColumns)
} }
@ -125,17 +126,17 @@ class PhotoDisplayDateAdapter(
} }
} }
object ItemDiffCallback : DiffUtil.ItemCallback<Pair<String, List<ResultPhotosFiles>>>() { object ItemDiffCallback : DiffUtil.ItemCallback<Pair<String, List<ResultDataFiles>>>() {
override fun areItemsTheSame( override fun areItemsTheSame(
oldItem: Pair<String, List<ResultPhotosFiles>>, oldItem: Pair<String, List<ResultDataFiles>>,
newItem: Pair<String, List<ResultPhotosFiles>> newItem: Pair<String, List<ResultDataFiles>>
): Boolean { ): Boolean {
return oldItem.first == newItem.first return oldItem.first == newItem.first
} }
override fun areContentsTheSame( override fun areContentsTheSame(
oldItem: Pair<String, List<ResultPhotosFiles>>, oldItem: Pair<String, List<ResultDataFiles>>,
newItem: Pair<String, List<ResultPhotosFiles>> newItem: Pair<String, List<ResultDataFiles>>
): Boolean { ): Boolean {
return oldItem == newItem return oldItem == newItem
} }

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@ -7,7 +7,6 @@ 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
@ -22,7 +21,9 @@ import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.NewBaseAdapter import com.ux.video.file.filerecovery.base.NewBaseAdapter
import com.ux.video.file.filerecovery.databinding.FileSpanCountThreeAdapterBinding import com.ux.video.file.filerecovery.databinding.FileSpanCountThreeAdapterBinding
import com.ux.video.file.filerecovery.databinding.FileSpanCountTwoAdapterBinding import com.ux.video.file.filerecovery.databinding.FileSpanCountTwoAdapterBinding
import com.ux.video.file.filerecovery.databinding.OneAudioDocumentsItemBinding import com.ux.video.file.filerecovery.databinding.OneAudioItemBinding
import com.ux.video.file.filerecovery.databinding.OneDocumentsItemBinding
import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.CustomTextView import com.ux.video.file.filerecovery.utils.CustomTextView
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
@ -40,23 +41,25 @@ class PhotoDisplayDateChildAdapter(
* @param addOrRemove 选中还是取消选中 * @param addOrRemove 选中还是取消选中
* @param dateAllSelected 这组数据是否全部选中某一天 * @param dateAllSelected 这组数据是否全部选中某一天
*/ */
var onSelectedUpdate: (resultPhotosFiles: ResultPhotosFiles, addOrRemove: Boolean, dateAllSelected: Boolean) -> Unit, var onSelectedUpdate: (resultDataFiles: ResultDataFiles, addOrRemove: Boolean, dateAllSelected: Boolean) -> Unit,
var hideThumbnailsUpdate: (dateAllSelected: Boolean) -> Unit, var hideThumbnailsUpdate: (dateAllSelected: Boolean) -> Unit,
var clickItem: (item: ResultPhotosFiles) -> Unit var clickItem: (item: ResultDataFiles) -> Unit
) : ) :
NewBaseAdapter<ResultPhotosFiles>(mContext) { NewBaseAdapter<ResultDataFiles>(mContext) {
//日期组某一天的数据选择状态维护 //日期组某一天的数据选择状态维护
val dateSelectedMap = mutableSetOf<ResultPhotosFiles>() val dateSelectedMap = mutableSetOf<ResultDataFiles>()
companion object { companion object {
//音频或者文档 //视频和图片支持布局切换 2/3/4列
private const val TYPE_ONE = 1
private const val TYPE_TWO = 2 private const val TYPE_TWO = 2
private const val TYPE_THREE = 3 private const val TYPE_THREE = 3
private const val TYPE_FOUR = 4 private const val TYPE_FOUR = 4
private const val TYPE_AUDIO = 5
private const val TYPE_DOCUMENTS = 6
} }
fun setAllSelected(isAdd: Boolean) { fun setAllSelected(isAdd: Boolean) {
@ -69,7 +72,11 @@ class PhotoDisplayDateChildAdapter(
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
when (scanType) { when (scanType) {
Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio -> { Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio -> {
return TYPE_ONE return TYPE_AUDIO
}
Common.VALUE_SCAN_TYPE_documents, Common.VALUE_SCAN_TYPE_deleted_documents -> {
return TYPE_DOCUMENTS
} }
else -> { else -> {
@ -110,8 +117,16 @@ class PhotoDisplayDateChildAdapter(
): RecyclerView.ViewHolder { ): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
return when (viewType) { return when (viewType) {
TYPE_ONE -> OneHolder( TYPE_AUDIO -> AudioHolder(
OneAudioDocumentsItemBinding.inflate( OneAudioItemBinding.inflate(
inflater,
parent,
false
)
)
TYPE_DOCUMENTS -> DocumentsHolder(
OneDocumentsItemBinding.inflate(
inflater, inflater,
parent, parent,
false false
@ -139,32 +154,36 @@ class PhotoDisplayDateChildAdapter(
override fun onBind( override fun onBind(
holder: RecyclerView.ViewHolder, holder: RecyclerView.ViewHolder,
item: ResultPhotosFiles, item: ResultDataFiles,
viewType: Int viewType: Int
) { ) {
when (holder) { when (holder) {
is TwoHolder -> holder.vb.run { is TwoHolder -> holder.vb.run {
initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item, imageType) initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item, imageType)
} }
is ThreeHolder -> holder.vb.run { is ThreeHolder -> holder.vb.run {
initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item, imageType) initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item, imageType)
} }
is OneHolder -> {
is AudioHolder -> {
item.run { item.run {
holder.vb.let { holder.vb.let {
it.textName.text = name it.textName.text = name
it.textDuration.text = Common.formatDuration(duration) it.textDuration.text = Common.formatDuration(duration)
it.textSize.text = sizeString it.textSize.text = sizeString
viewModel.checkIsSelect(this).let { isSelected ->
it.imageSelect.isSelected = isSelected initAudioDocuments(it.imageSelect,this)
addOrRemove(this, isSelected) // viewModel.checkIsSelect(this).let { isSelected ->
} // it.imageSelect.isSelected = isSelected
it.imageSelect.setOnClickListener { // addOrRemove(this, isSelected)
it.isSelected = !it.isSelected // }
it.isSelected.let { newStatus -> // it.imageSelect.setOnClickListener {
addOrRemove(this, newStatus) // it.isSelected = !it.isSelected
} // it.isSelected.let { newStatus ->
} // addOrRemove(this, newStatus)
// }
// }
it.constraintLayout.setOnClickListener { it.constraintLayout.setOnClickListener {
clickItem(this) clickItem(this)
} }
@ -172,6 +191,28 @@ class PhotoDisplayDateChildAdapter(
} }
} }
} }
is DocumentsHolder -> {
item.run {
holder.vb.let {
it.textName.text = name
it.textDate.text = Common.getItemMonthDay(lastModified)
it.textSize.text = sizeString
targetFile?.let { file->
it.imageIcon.setImageResource(Common.getFileIconRes(file))
}
initAudioDocuments(it.imageSelect,this)
it.constraintLayout.setOnClickListener {
clickItem(this)
}
}
}
}
} }
} }
@ -182,29 +223,45 @@ class PhotoDisplayDateChildAdapter(
class TwoHolder(val vb: FileSpanCountTwoAdapterBinding) : class TwoHolder(val vb: FileSpanCountTwoAdapterBinding) :
RecyclerView.ViewHolder(vb.root) RecyclerView.ViewHolder(vb.root)
class OneHolder(val vb: OneAudioDocumentsItemBinding) : class AudioHolder(val vb: OneAudioItemBinding) :
RecyclerView.ViewHolder(vb.root) RecyclerView.ViewHolder(vb.root)
class DocumentsHolder(val vb: OneDocumentsItemBinding) :
RecyclerView.ViewHolder(vb.root)
private fun initAudioDocuments(imageSelect: ImageView,item: ResultDataFiles){
viewModel.checkIsSelect(item).let { isSelected ->
imageSelect.isSelected = isSelected
addOrRemove(item, isSelected)
}
imageSelect.setOnClickListener {
it.isSelected = !it.isSelected
it.isSelected.let { newStatus ->
addOrRemove(item, newStatus)
}
}
}
private fun initDateView( private fun initDateView(
rootLayout: RelativeLayout, rootLayout: RelativeLayout,
imageSelectStatus: ImageView, imageSelectStatus: ImageView,
textSize: CustomTextView, textSize: CustomTextView,
imageThumbnail: ImageView, imageThumbnail: ImageView,
item: ResultPhotosFiles, item: ResultDataFiles,
imageType: ImageView imageType: ImageView
) { ) {
item.run { item.run {
initAudioDocuments(imageSelectStatus,this)
viewModel.checkIsSelect(this).let { // viewModel.checkIsSelect(this).let {
imageSelectStatus.isSelected = it // imageSelectStatus.isSelected = it
addOrRemove(this, 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(this, newStatus) // addOrRemove(this, newStatus)
} // }
} // }
textSize.text = sizeString textSize.text = sizeString
imageType.setImageResource( imageType.setImageResource(
@ -254,13 +311,13 @@ class PhotoDisplayDateChildAdapter(
} }
private fun addOrRemove(resultPhotosFiles: ResultPhotosFiles, boolean: Boolean) { private fun addOrRemove(resultDataFiles: ResultDataFiles, boolean: Boolean) {
if (boolean) { if (boolean) {
dateSelectedMap.add(resultPhotosFiles) dateSelectedMap.add(resultDataFiles)
} else { } else {
dateSelectedMap.remove(resultPhotosFiles) dateSelectedMap.remove(resultDataFiles)
} }
onSelectedUpdate.invoke(resultPhotosFiles, boolean, dateSelectedMap.size == data.size) onSelectedUpdate.invoke(resultDataFiles, boolean, dateSelectedMap.size == data.size)
} }

View File

@ -1,17 +1,13 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.content.Intent import android.content.Intent
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build import android.os.Build
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.load.engine.GlideException
@ -23,6 +19,7 @@ import com.bumptech.glide.request.target.Target
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityPhotoInfoBinding import com.ux.video.file.filerecovery.databinding.ActivityPhotoInfoBinding
import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.success.RecoverySuccessActivity import com.ux.video.file.filerecovery.success.RecoverySuccessActivity
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE
@ -46,15 +43,15 @@ class PhotoInfoActivity : BaseActivity<ActivityPhotoInfoBinding>() {
} }
private var scanType: Int = VALUE_SCAN_TYPE_photo private var scanType: Int = VALUE_SCAN_TYPE_photo
private var myData: ResultPhotosFiles? = null private var myData: ResultDataFiles? = null
private lateinit var player: ExoPlayer
override fun inflateBinding(inflater: LayoutInflater): ActivityPhotoInfoBinding = override fun inflateBinding(inflater: LayoutInflater): ActivityPhotoInfoBinding =
ActivityPhotoInfoBinding.inflate(inflater) ActivityPhotoInfoBinding.inflate(inflater)
override fun initView() { override fun initView() {
super.initView() super.initView()
myData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { myData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(KEY_CLICK_ITEM, ResultPhotosFiles::class.java) intent.getParcelableExtra(KEY_CLICK_ITEM, ResultDataFiles::class.java)
} else { } else {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
intent.getParcelableExtra(KEY_CLICK_ITEM) intent.getParcelableExtra(KEY_CLICK_ITEM)
@ -77,6 +74,9 @@ class PhotoInfoActivity : BaseActivity<ActivityPhotoInfoBinding>() {
tvDate.text = Common.getFormatDate(resultPhotosFiles.lastModified) tvDate.text = Common.getFormatDate(resultPhotosFiles.lastModified)
tvResolution.text = resultPhotosFiles.resolution tvResolution.text = resultPhotosFiles.resolution
tvDuration.text = Common.formatDuration(resultPhotosFiles.duration) tvDuration.text = Common.formatDuration(resultPhotosFiles.duration)
resultPhotosFiles.targetFile?.let {
tvType.text = Common.getMimeTypeParts(it)
}
layoutBottom.tvLeft.run { layoutBottom.tvLeft.run {
text = resources.getString(R.string.delete) text = resources.getString(R.string.delete)
setOnClickListener { setOnClickListener {
@ -161,14 +161,8 @@ class PhotoInfoActivity : BaseActivity<ActivityPhotoInfoBinding>() {
layoutSeekbar.isVisible = true layoutSeekbar.isVisible = true
imPlay.isVisible = true imPlay.isVisible = true
frameImage.setBackgroundResource(R.drawable.bg_info_music_f2f2f7_8) frameImage.setBackgroundResource(R.drawable.bg_info_music_f2f2f7_8)
image.setImageResource(R.drawable.image_info_music)
val params = image.layoutParams ?: ViewGroup.LayoutParams( loadCenterImage(image,R.drawable.image_info_music)
180.dpToPx(this@PhotoInfoActivity),
180.dpToPx(this@PhotoInfoActivity)
)
params.width = 180.dpToPx(this@PhotoInfoActivity)
params.height = 180.dpToPx(this@PhotoInfoActivity)
image.layoutParams = params
initPlayAudio() initPlayAudio()
layoutResolution.isVisible = false layoutResolution.isVisible = false
@ -182,15 +176,29 @@ class PhotoInfoActivity : BaseActivity<ActivityPhotoInfoBinding>() {
layoutSize.isVisible = true layoutSize.isVisible = true
layoutDate.isVisible = true layoutDate.isVisible = true
layoutDuration.isVisible = false frameImage.setBackgroundResource(R.drawable.bg_info_music_f2f2f7_8)
myData?.targetFile?.let {
loadCenterImage(image, Common.getFileIconRes(it))
}
imPlay.isVisible = false
layoutSeekbar.isVisible = false
layoutResolution.isVisible = false
layoutDuration.isVisible = false layoutDuration.isVisible = false
} }
} }
}
} }
private fun loadCenterImage(image: ImageView,drawableId: Int){
image.setImageResource(drawableId)
val params = image.layoutParams ?: ViewGroup.LayoutParams(
180.dpToPx(this@PhotoInfoActivity),
180.dpToPx(this@PhotoInfoActivity)
)
params.width = 180.dpToPx(this@PhotoInfoActivity)
params.height = 180.dpToPx(this@PhotoInfoActivity)
image.layoutParams = params
} }
private fun loadImage(image: ImageView,file: File){ private fun loadImage(image: ImageView,file: File){
Glide.with(this@PhotoInfoActivity) Glide.with(this@PhotoInfoActivity)
.load(file) .load(file)

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.content.Intent import android.content.Intent
import android.text.Editable import android.text.Editable
@ -14,6 +14,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityPhotoSortingBinding import com.ux.video.file.filerecovery.databinding.ActivityPhotoSortingBinding
import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.success.RecoverySuccessActivity import com.ux.video.file.filerecovery.success.RecoverySuccessActivity
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE
@ -97,21 +98,21 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
private var filterLayoutPopupWindows: FilterPopupWindows? = null private var filterLayoutPopupWindows: FilterPopupWindows? = null
private lateinit var sortBySizeBigToSmall: List<ResultPhotosFiles> private lateinit var sortBySizeBigToSmall: List<ResultDataFiles>
private lateinit var sortBySizeSmallToBig: List<ResultPhotosFiles> private lateinit var sortBySizeSmallToBig: List<ResultDataFiles>
private lateinit var sortByDateReverse: List<Pair<String, List<ResultPhotosFiles>>> private lateinit var sortByDateReverse: List<Pair<String, List<ResultDataFiles>>>
private lateinit var sortedByDatePositive: List<Pair<String, List<ResultPhotosFiles>>> private lateinit var sortedByDatePositive: List<Pair<String, List<ResultDataFiles>>>
//最新显示的数据集合(包含缩略图 ,只保存当前筛选后或者排序后显示的数据不受switch切换影响 //最新显示的数据集合(包含缩略图 ,只保存当前筛选后或者排序后显示的数据不受switch切换影响
private var currentDateList: List<Pair<String, List<ResultPhotosFiles>>>? = null private var currentDateList: List<Pair<String, List<ResultDataFiles>>>? = null
private var currentSizeList: List<ResultPhotosFiles>? = null private var currentSizeList: List<ResultDataFiles>? = null
//选中的所有数据集合(实际选中) //选中的所有数据集合(实际选中)
private lateinit var allSelectedSetList: Set<ResultPhotosFiles> private lateinit var allSelectedSetList: Set<ResultDataFiles>
//选中的所有数据集合(筛选后的数据实际显示的所有选中) //选中的所有数据集合(筛选后的数据实际显示的所有选中)
private lateinit var filterSelectedSetList: Set<ResultPhotosFiles> private lateinit var filterSelectedSetList: Set<ResultDataFiles>
private lateinit var mItemDecoration: GridSpacingItemDecoration private lateinit var mItemDecoration: GridSpacingItemDecoration
@ -125,7 +126,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
override fun initData() { override fun initData() {
super.initData() super.initData()
scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo)
val list: ArrayList<ResultPhotosFiles>? = val list: ArrayList<ResultDataFiles>? =
intent.getParcelableArrayListExtraCompat(KEY_PHOTO_FOLDER_FILE) intent.getParcelableArrayListExtraCompat(KEY_PHOTO_FOLDER_FILE)
mItemDecoration = mItemDecoration =
GridSpacingItemDecoration(columns, Common.itemSpacing, Common.horizontalSpacing) GridSpacingItemDecoration(columns, Common.itemSpacing, Common.horizontalSpacing)
@ -338,7 +339,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
is PhotoDisplayDateAdapter -> { is PhotoDisplayDateAdapter -> {
dateAdapter?.setAllSelected(it.isSelected) dateAdapter?.setAllSelected(it.isSelected)
dateAdapter?.getCurrentData()?.let { dateAdapter?.getCurrentData()?.let {
it as List<Pair<String, List<ResultPhotosFiles>>> it as List<Pair<String, List<ResultDataFiles>>>
if (it.size > 0) if (it.size > 0)
Common.showLog("------------全选按钮 日期-${it.size} ${it[0].second[0].path}") Common.showLog("------------全选按钮 日期-${it.size} ${it[0].second[0].path}")
} }
@ -348,7 +349,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
is PhotoDisplayDateChildAdapter -> { is PhotoDisplayDateChildAdapter -> {
sizeSortAdapter?.setAllSelected(it.isSelected) sizeSortAdapter?.setAllSelected(it.isSelected)
sizeSortAdapter?.getCurrentData()?.let { sizeSortAdapter?.getCurrentData()?.let {
it as List<ResultPhotosFiles> it as List<ResultDataFiles>
if (it.size > 0) if (it.size > 0)
Common.showLog("------------全选按钮 大小-${it.size} ${it[0].path}") Common.showLog("------------全选按钮 大小-${it.size} ${it[0].path}")
} }
@ -437,12 +438,12 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
private fun initGetCurrentSizeList(): List<ResultPhotosFiles> { private fun initGetCurrentSizeList(): List<ResultDataFiles> {
currentSizeList = currentSizeList ?: currentDateList?.flatMap { it.second } currentSizeList = currentSizeList ?: currentDateList?.flatMap { it.second }
return currentSizeList!! return currentSizeList!!
} }
private fun resetCurrentSizeList(currentList: List<ResultPhotosFiles>) { private fun resetCurrentSizeList(currentList: List<ResultDataFiles>) {
currentSizeList = currentList currentSizeList = currentList
currentDateList = null currentDateList = null
@ -451,12 +452,12 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
private fun initGetCurrentDateList(): List<Pair<String, List<ResultPhotosFiles>>> { private fun initGetCurrentDateList(): List<Pair<String, List<ResultDataFiles>>> {
currentDateList = currentDateList ?: Common.getSortByDayNewToOldInit(currentSizeList!!) currentDateList = currentDateList ?: Common.getSortByDayNewToOldInit(currentSizeList!!)
return currentDateList!! return currentDateList!!
} }
private fun resetCurrentDateList(currentList: List<Pair<String, List<ResultPhotosFiles>>>) { private fun resetCurrentDateList(currentList: List<Pair<String, List<ResultDataFiles>>>) {
currentDateList = currentList currentDateList = currentList
currentSizeList = null currentSizeList = null
val totalSelectedCount = currentList.sumOf { pair -> val totalSelectedCount = currentList.sumOf { pair ->
@ -484,7 +485,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
val aPx = 16.dpToPx(context) val aPx = 16.dpToPx(context)
val bottom = 70.dpToPx(context) val bottom = 70.dpToPx(context)
when (scanType) { when (scanType) {
VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> { VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio,VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> {
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
setPadding(aPx, 0, 0, bottom) setPadding(aPx, 0, 0, bottom)
} }
@ -713,8 +714,8 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
* 数据筛选或者缩略图切换显示后对比刷新实际显示的选中数据 * 数据筛选或者缩略图切换显示后对比刷新实际显示的选中数据
*/ */
private fun checkRefreshDisPlaySelected( private fun checkRefreshDisPlaySelected(
list1: List<Pair<String, List<ResultPhotosFiles>>>? = null, list1: List<Pair<String, List<ResultDataFiles>>>? = null,
list2: List<ResultPhotosFiles>? = null list2: List<ResultDataFiles>? = null
) { ) {
lifecycleScope.launch { lifecycleScope.launch {
list1?.let { list1?.let {
@ -887,13 +888,13 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
//选中集合的更新 //选中集合的更新
viewModel.afterDeleted() viewModel.afterDeleted()
deferredResults["sizeList"]?.let { list -> deferredResults["sizeList"]?.let { list ->
list as List<ResultPhotosFiles> list as List<ResultDataFiles>
Common.showLog("---------更新 sizeList = ${list.size}") Common.showLog("---------更新 sizeList = ${list.size}")
sizeSortAdapter?.setData(list) sizeSortAdapter?.setData(list)
resetCurrentSizeList(list) resetCurrentSizeList(list)
} }
deferredResults["dateList"]?.let { list -> deferredResults["dateList"]?.let { list ->
list as List<Pair<String, List<ResultPhotosFiles>>> list as List<Pair<String, List<ResultDataFiles>>>
Common.showLog("---------更新 dateList = ${list.size}") Common.showLog("---------更新 dateList = ${list.size}")
dateAdapter?.setData(list) dateAdapter?.setData(list)
resetCurrentDateList(list) resetCurrentDateList(list)
@ -932,13 +933,13 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
//选中集合的更新 //选中集合的更新
viewModel.afterSingleDeleted(deletedData) viewModel.afterSingleDeleted(deletedData)
deferredResults["sizeList"]?.let { list -> deferredResults["sizeList"]?.let { list ->
list as List<ResultPhotosFiles> list as List<ResultDataFiles>
Common.showLog("---------更新 sizeList = ${list.size}") Common.showLog("---------更新 sizeList = ${list.size}")
sizeSortAdapter?.setData(list) sizeSortAdapter?.setData(list)
resetCurrentSizeList(list) resetCurrentSizeList(list)
} }
deferredResults["dateList"]?.let { list -> deferredResults["dateList"]?.let { list ->
list as List<Pair<String, List<ResultPhotosFiles>>> list as List<Pair<String, List<ResultDataFiles>>>
Common.showLog("---------更新 dateList = ${list.size}") Common.showLog("---------更新 dateList = ${list.size}")
dateAdapter?.setData(list) dateAdapter?.setData(list)
resetCurrentDateList(list) resetCurrentDateList(list)

View File

@ -1,9 +1,9 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.app.Activity
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import com.ux.video.file.filerecovery.db.ObjectBoxManager
import com.ux.video.file.filerecovery.db.ResultDataFiles
import androidx.lifecycle.lifecycleScope
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.ScanManager 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
@ -18,9 +18,9 @@ object RecoverOrDeleteManager {
private var dialogConfirmDelete: ConfirmDeleteDialogFragment? = null private var dialogConfirmDelete: ConfirmDeleteDialogFragment? = null
//详情页面进行删除操作的监听 //详情页面进行删除操作的监听
private var onSingleDeletedCompleteListener: ((ResultPhotosFiles) -> Unit)? = null private var onSingleDeletedCompleteListener: ((ResultDataFiles) -> Unit)? = null
fun setOnSingleDeleteCompleteListener(listener: (ResultPhotosFiles) -> Unit) { fun setOnSingleDeleteCompleteListener(listener: (ResultDataFiles) -> Unit) {
onSingleDeletedCompleteListener = listener onSingleDeletedCompleteListener = listener
} }
@ -31,21 +31,10 @@ object RecoverOrDeleteManager {
fun showRecoveringDialog( fun showRecoveringDialog(
fragmentManager: FragmentManager, fragmentManager: FragmentManager,
scope: CoroutineScope, scope: CoroutineScope,
selectedSetList: Set<ResultPhotosFiles>, selectedSetList: Set<ResultDataFiles>,
onComplete: (number: Int) -> Unit onComplete: (number: Int) -> Unit
) { ) {
scope.copySelectedFilesAsync(
selectedSet = selectedSetList,
folder = Common.recoveryPhotoDir,
onProgress = { currentCounts: Int, fileName: String, success: Boolean ->
ScanManager.showLog("--------恢复图片 ", "----------${currentCounts} ${fileName}")
dialogRecovering?.updateProgress(currentCounts)
}) { counts ->
dialogRecovering?.updateProgress(counts)
ScanManager.showLog("--------恢复图片 ", "----------恢复完成 ${counts}")
}
dialogRecovering = dialogRecovering ?: RecoveringDialogFragment() dialogRecovering = dialogRecovering ?: RecoveringDialogFragment()
dialogRecovering?.run { dialogRecovering?.run {
total = selectedSetList.size total = selectedSetList.size
@ -55,6 +44,21 @@ object RecoverOrDeleteManager {
} }
show(fragmentManager, "") show(fragmentManager, "")
} }
scope.copySelectedFilesAsync(
selectedSet = selectedSetList,
folder = Common.recoveryPhotoDir,
onProgress = { currentCounts: Int, data: ResultDataFiles, fileName: String, success: Boolean ->
if(success){
ScanManager.showLog("--------恢复图片 ", "----------${currentCounts} ${fileName}")
dialogRecovering?.updateProgress(currentCounts)
ObjectBoxManager.addRecoveryFile(data)
}
}) { counts ->
dialogRecovering?.updateProgress(counts)
ScanManager.showLog("--------恢复图片 ", "----------恢复完成 ${counts}")
}
} }
/** /**
@ -64,7 +68,7 @@ object RecoverOrDeleteManager {
isInfoDelete: Boolean = false, isInfoDelete: Boolean = false,
fragmentManager: FragmentManager, fragmentManager: FragmentManager,
scope: CoroutineScope, scope: CoroutineScope,
selectedSetList: Set<ResultPhotosFiles>, selectedSetList: Set<ResultDataFiles>,
onComplete: (number: Int) -> Unit onComplete: (number: Int) -> Unit
) { ) {
dialogConfirmDelete = dialogConfirmDelete ?: ConfirmDeleteDialogFragment() dialogConfirmDelete = dialogConfirmDelete ?: ConfirmDeleteDialogFragment()
@ -86,18 +90,10 @@ object RecoverOrDeleteManager {
isInfoDelete: Boolean = false, isInfoDelete: Boolean = false,
fragmentManager: FragmentManager, fragmentManager: FragmentManager,
scope: CoroutineScope, scope: CoroutineScope,
selectedSetList: Set<ResultPhotosFiles>, selectedSetList: Set<ResultDataFiles>,
onComplete: (number: Int) -> Unit onComplete: (number: Int) -> Unit
) { ) {
scope.deleteFilesAsync(
selectedSet = selectedSetList,
onProgress = { currentCounts: Int, path: String, success: Boolean ->
ScanManager.showLog("--------删除图片 ", "----------${currentCounts} ${path}")
dialogDeleting?.updateProgress(currentCounts)
}) { counts ->
dialogDeleting?.updateProgress(counts)
ScanManager.showLog("--------恢复图片 ", "----------恢复完成 ${counts}")
}
dialogDeleting = dialogDeleting ?: DeletingDialogFragment() dialogDeleting = dialogDeleting ?: DeletingDialogFragment()
dialogDeleting?.run { dialogDeleting?.run {
total = selectedSetList.size total = selectedSetList.size
@ -110,5 +106,18 @@ object RecoverOrDeleteManager {
} }
show(fragmentManager, "") show(fragmentManager, "")
} }
scope.deleteFilesAsync(
selectedSet = selectedSetList,
onProgress = { currentCounts: Int, data: ResultDataFiles, path: String, success: Boolean ->
if (success){
ScanManager.showLog("--------删除图片 ", "----------${currentCounts} ${path}")
dialogDeleting?.updateProgress(currentCounts)
ObjectBoxManager.deleteRecoveryFile(data)
}
}) { counts ->
dialogDeleting?.updateProgress(counts)
ScanManager.showLog("--------恢复图片 ", "----------恢复完成 ${counts}")
}
} }
} }

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseIngDialogFragment import com.ux.video.file.filerecovery.base.BaseIngDialogFragment

View File

@ -1,4 +1,4 @@
package com.ux.video.file.filerecovery.photo package com.ux.video.file.filerecovery.sort
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle

View File

@ -9,14 +9,13 @@ import android.os.Environment
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.webkit.MimeTypeMap
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData
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.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File
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,24 +39,25 @@ object Common {
val rootDir = Environment.getExternalStorageDirectory() val rootDir = Environment.getExternalStorageDirectory()
val dateFormat = SimpleDateFormat("MMMM d,yyyy", Locale.ENGLISH) val dateFormat = SimpleDateFormat("MMMM d,yyyy", Locale.ENGLISH)
val chineseFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINESE) val chineseFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINESE)
val itemDateFormat = SimpleDateFormat("MM-dd", Locale.CHINESE)
val recoveryPhotoDir = "MyAllRecovery/Photo" val recoveryPhotoDir = "MyAllRecovery/Photo"
/** /**
* 默认按照日期分类将最新的排前面 降序 * 默认按照日期分类将最新的排前面 降序
*/ */
fun getSortByDayNewToOldInit(list: List<ResultPhotosFiles>): List<Pair<String, List<ResultPhotosFiles>>> { fun getSortByDayNewToOldInit(list: List<ResultDataFiles>): List<Pair<String, List<ResultDataFiles>>> {
val grouped = list.groupBy { val grouped = list.groupBy {
dateFormat.format(Date(it.lastModified)) dateFormat.format(Date(it.lastModified))
} }
val parentData: List<Pair<String, List<ResultPhotosFiles>>> = grouped val parentData: List<Pair<String, List<ResultDataFiles>>> = grouped
.map { it.key to it.value } .map { it.key to it.value }
.sortedByDescending { dateFormat.parse(it.first)?.time ?: 0L } .sortedByDescending { dateFormat.parse(it.first)?.time ?: 0L }
return parentData return parentData
} }
fun getSortByDayNewToOld( fun getSortByDayNewToOld(
list: List<Pair<String, List<ResultPhotosFiles>>> list: List<Pair<String, List<ResultDataFiles>>>
): List<Pair<String, List<ResultPhotosFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
return list.sortedByDescending { pair -> return list.sortedByDescending { pair ->
dateFormat.parse(pair.first)?.time ?: 0L dateFormat.parse(pair.first)?.time ?: 0L
} }
@ -68,28 +68,28 @@ object Common {
* 按照日期排序 时间最早的排前面 升序 * 按照日期排序 时间最早的排前面 升序
* *
*/ */
fun getSortByDayOldToNew(list: List<Pair<String, List<ResultPhotosFiles>>>) = fun getSortByDayOldToNew(list: List<Pair<String, List<ResultDataFiles>>>) =
list.sortedBy { dateFormat.parse(it.first)?.time ?: 0L } list.sortedBy { dateFormat.parse(it.first)?.time ?: 0L }
/** /**
* 按照文件大小排序将最大的排前面 降序 * 按照文件大小排序将最大的排前面 降序
*/ */
fun getSortBySizeBigToSmall(list: List<ResultPhotosFiles>) = list.sortedByDescending { fun getSortBySizeBigToSmall(list: List<ResultDataFiles>) = list.sortedByDescending {
it.size it.size
} }
/** /**
* 按照文件大小排序将最小的排前面 升序 * 按照文件大小排序将最小的排前面 升序
*/ */
fun getSortBySizeSmallToBig(list: List<ResultPhotosFiles>) = list.sortedBy { fun getSortBySizeSmallToBig(list: List<ResultDataFiles>) = list.sortedBy {
it.size it.size
} }
fun searchByName( fun searchByName(
data: List<Pair<String, List<ResultPhotosFiles>>>, data: List<Pair<String, List<ResultDataFiles>>>,
keyword: String keyword: String
): List<Pair<String, List<ResultPhotosFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
if (keyword.isBlank()) return data if (keyword.isBlank()) return data
return data.mapNotNull { (key, files) -> return data.mapNotNull { (key, files) ->
@ -98,9 +98,9 @@ object Common {
} }
} }
fun searchByNameList( fun searchByNameList(
list: List<ResultPhotosFiles>, list: List<ResultDataFiles>,
keyword: String keyword: String
): List<ResultPhotosFiles> { ): List<ResultDataFiles> {
if (keyword.isBlank()) return list if (keyword.isBlank()) return list
return list.filter { it.name.contains(keyword, ignoreCase = true) } return list.filter { it.name.contains(keyword, ignoreCase = true) }
} }
@ -135,8 +135,8 @@ object Common {
* @param months 筛选months月之内的数据 * @param months 筛选months月之内的数据
*/ */
fun filterWithinOneMonthByDay( fun filterWithinOneMonthByDay(
grouped: List<Pair<String, List<ResultPhotosFiles>>>, months: Int grouped: List<Pair<String, List<ResultDataFiles>>>, months: Int
): List<Pair<String, List<ResultPhotosFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
val today = Calendar.getInstance() val today = Calendar.getInstance()
val oneMonthAgo = Calendar.getInstance().apply { val oneMonthAgo = Calendar.getInstance().apply {
add(Calendar.MONTH, -months) // 1 个月前 add(Calendar.MONTH, -months) // 1 个月前
@ -151,7 +151,7 @@ object Common {
/** /**
* @param months 筛选months月之内的数据 * @param months 筛选months月之内的数据
*/ */
fun filterWithinOneMonth(list: List<ResultPhotosFiles>, months: Int): List<ResultPhotosFiles> { fun filterWithinOneMonth(list: List<ResultDataFiles>, months: Int): List<ResultDataFiles> {
val today = Calendar.getInstance() val today = Calendar.getInstance()
val oneMonthAgo = Calendar.getInstance().apply { val oneMonthAgo = Calendar.getInstance().apply {
add(Calendar.MONTH, -months) add(Calendar.MONTH, -months)
@ -184,10 +184,10 @@ object Common {
* @return 显示的选中数量和选中集合 * @return 显示的选中数量和选中集合
*/ */
fun checkSelectListContainDate( fun checkSelectListContainDate(
list: List<Pair<String, List<ResultPhotosFiles>>>, list: List<Pair<String, List<ResultDataFiles>>>,
selected: Set<ResultPhotosFiles> selected: Set<ResultDataFiles>
): Pair<Int, MutableSet<ResultPhotosFiles>> { ): Pair<Int, MutableSet<ResultDataFiles>> {
val currentSelected = mutableSetOf<ResultPhotosFiles>() val currentSelected = mutableSetOf<ResultDataFiles>()
val totalSelectedCount = list.sumOf { pair -> val totalSelectedCount = list.sumOf { pair ->
pair.second.count { pair.second.count {
@ -202,10 +202,10 @@ object Common {
} }
suspend fun checkSelectListContainDateAsync( suspend fun checkSelectListContainDateAsync(
list: List<Pair<String, List<ResultPhotosFiles>>>, list: List<Pair<String, List<ResultDataFiles>>>,
selected: Set<ResultPhotosFiles> selected: Set<ResultDataFiles>
): Pair<Int, MutableSet<ResultPhotosFiles>> = withContext(Dispatchers.Default) { ): Pair<Int, MutableSet<ResultDataFiles>> = withContext(Dispatchers.Default) {
val currentSelected = mutableSetOf<ResultPhotosFiles>() val currentSelected = mutableSetOf<ResultDataFiles>()
var totalSelectedCount = 0 var totalSelectedCount = 0
// 高效遍历外层 + 内层列表 // 高效遍历外层 + 内层列表
@ -238,10 +238,10 @@ object Common {
suspend fun checkSelectListContainSize( suspend fun checkSelectListContainSize(
list: List<ResultPhotosFiles>, list: List<ResultDataFiles>,
selected: Set<ResultPhotosFiles> selected: Set<ResultDataFiles>
): Pair<Int, MutableSet<ResultPhotosFiles>> = withContext(Dispatchers.Default) { ): Pair<Int, MutableSet<ResultDataFiles>> = withContext(Dispatchers.Default) {
val currentSelected = mutableSetOf<ResultPhotosFiles>() val currentSelected = mutableSetOf<ResultDataFiles>()
var totalSelectedCount = 0 var totalSelectedCount = 0
// 高效遍历外层 + 内层列表 // 高效遍历外层 + 内层列表
@ -259,8 +259,8 @@ object Common {
* 去掉缩略图的集合 * 去掉缩略图的集合
*/ */
suspend fun filterThumbnailsAsync( suspend fun filterThumbnailsAsync(
originalList: MutableList<Pair<String, List<ResultPhotosFiles>>> originalList: MutableList<Pair<String, List<ResultDataFiles>>>
): List<Pair<String, List<ResultPhotosFiles>>> = withContext(Dispatchers.Default) { ): List<Pair<String, List<ResultDataFiles>>> = withContext(Dispatchers.Default) {
originalList.asSequence() originalList.asSequence()
.map { (key, files) -> .map { (key, files) ->
key to files.asSequence().filter { !it.isThumbnail }.toList() key to files.asSequence().filter { !it.isThumbnail }.toList()
@ -271,9 +271,9 @@ object Common {
fun removeSelectedFromList( fun removeSelectedFromList(
list: List<Pair<String, List<ResultPhotosFiles>>>, list: List<Pair<String, List<ResultDataFiles>>>,
selectedLiveData: Set<ResultPhotosFiles> selectedLiveData: Set<ResultDataFiles>
): List<Pair<String, List<ResultPhotosFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
return list.mapNotNull { (key, files) -> return list.mapNotNull { (key, files) ->
val filtered = files.filterNot { it in selectedLiveData } val filtered = files.filterNot { it in selectedLiveData }
if (filtered.isNotEmpty()) key to filtered else null if (filtered.isNotEmpty()) key to filtered else null
@ -281,9 +281,9 @@ object Common {
} }
fun removeSelectedFromSizeList( fun removeSelectedFromSizeList(
list: List<ResultPhotosFiles>, list: List<ResultDataFiles>,
selectedLiveData: Set<ResultPhotosFiles> selectedLiveData: Set<ResultDataFiles>
): List<ResultPhotosFiles> { ): List<ResultDataFiles> {
return list.filterNot { it in selectedLiveData } return list.filterNot { it in selectedLiveData }
} }
@ -304,6 +304,7 @@ object Common {
} }
@SuppressLint("DefaultLocale")
fun formatDuration(ms: Long): String { fun formatDuration(ms: Long): String {
val totalSeconds = ms / 1000 val totalSeconds = ms / 1000
val hours = totalSeconds / 3600 val hours = totalSeconds / 3600
@ -316,11 +317,66 @@ object Common {
String.format("%02d:%02d", minutes, seconds) String.format("%02d:%02d", minutes, seconds)
} }
} }
fun getFileMIME(file: File): String {
val ext = file.extension.lowercase()
val mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext) ?: return "unknown"
return mime
return when {
mime.startsWith("image/") -> "image"
mime.startsWith("video/") -> "video"
mime.startsWith("audio/") -> "audio"
mime.startsWith("text/") -> "document"
mime == "application/pdf" -> "document"
mime.startsWith("application/vnd.openxmlformats") -> "document"
mime.startsWith("application/ms") -> "document"
mime == "application/zip" ||
mime == "application/x-rar-compressed" ||
mime == "application/x-7z-compressed" -> "archive"
else -> "other"
}
}
val customMimeMap = mapOf(
"xapk" to "application/zip",
// 可以继续添加其他自定义扩展名
)
fun getMimeTypeParts(file: File): String {
val extension = file.extension.lowercase()
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)?:customMimeMap[extension]
showLog("-----------ext=$extension mimeType=${mimeType}")
return if (mimeType != null && mimeType.contains("/")) {
val parts = mimeType.split("/")
val mainType = parts[0]
val subType = parts.getOrNull(1)
"$mainType/$subType"
} else {
"unknown"
}
}
fun getFileIconRes(file: File): Int {
val ext = file.extension.lowercase()
return when (ext) {
"doc", "docx" -> R.drawable.icon_doc
"xls", "xlsx" -> R.drawable.icon_xls
"ppt", "pptx" -> R.drawable.icon_ppt
"pdf" -> R.drawable.icon_pdf
"txt" -> R.drawable.icon_txt
"apk", "xapk" -> R.drawable.icon_apk
else -> R.drawable.icon_unknow
}
}
fun getFormatDate(time: Long): String { fun getFormatDate(time: Long): String {
return dateFormat.format(Date(time)) return dateFormat.format(Date(time))
} }
fun getItemMonthDay(time: Long): String{
return itemDateFormat.format(time)
}
fun getChineseFormatDate(date: Date): String { fun getChineseFormatDate(date: Date): String {
return chineseFormat.format(date) return chineseFormat.format(date)

View File

@ -7,7 +7,7 @@ import android.os.Build
import android.os.Parcelable 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.db.ResultDataFiles
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.Date import java.util.Date
@ -40,11 +40,11 @@ object ExtendFunctions {
} }
fun List<ResultPhotosFiles>.filterWithinDateRangeList( fun List<ResultDataFiles>.filterWithinDateRangeList(
months: Int = -1, months: Int = -1,
startDate: Date? = null, startDate: Date? = null,
endDate: Date? = null endDate: Date? = null
): List<ResultPhotosFiles> { ): List<ResultDataFiles> {
val today = Calendar.getInstance() val today = Calendar.getInstance()
@ -79,11 +79,11 @@ object ExtendFunctions {
fun List<Pair<String, List<ResultPhotosFiles>>>.filterWithinDateRange( fun List<Pair<String, List<ResultDataFiles>>>.filterWithinDateRange(
months: Int = -1, months: Int = -1,
startDate: Date? = null, startDate: Date? = null,
endDate: Date? = null endDate: Date? = null
): List<Pair<String, List<ResultPhotosFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
val sdf = Common.dateFormat val sdf = Common.dateFormat
val today = Calendar.getInstance() val today = Calendar.getInstance()
@ -118,10 +118,10 @@ object ExtendFunctions {
/** /**
* 按文件大小筛选区间 [minSize, maxSize] * 按文件大小筛选区间 [minSize, maxSize]
*/ */
fun List<ResultPhotosFiles>.filterBySizeList( fun List<ResultDataFiles>.filterBySizeList(
minSize: Long, minSize: Long,
maxSize: Long maxSize: Long
): List<ResultPhotosFiles> { ): List<ResultDataFiles> {
if (minSize == -1L) return this if (minSize == -1L) return this
return this.filter { it.size in minSize..maxSize } return this.filter { it.size in minSize..maxSize }
} }
@ -129,10 +129,10 @@ object ExtendFunctions {
/** /**
* 按文件大小筛选区间 [minSize, maxSize] * 按文件大小筛选区间 [minSize, maxSize]
*/ */
fun List<ResultPhotosFiles>.filterByDurationList( fun List<ResultDataFiles>.filterByDurationList(
minSize: Long, minSize: Long,
maxSize: Long maxSize: Long
): List<ResultPhotosFiles> { ): List<ResultDataFiles> {
if (minSize == -1L) return this if (minSize == -1L) return this
return this.filter { it.duration in minSize..maxSize } return this.filter { it.duration in minSize..maxSize }
} }
@ -140,10 +140,10 @@ object ExtendFunctions {
/** /**
* 分组数据按大小筛选 ,图片和文件筛选文件大小 * 分组数据按大小筛选 ,图片和文件筛选文件大小
*/ */
fun List<Pair<String, List<ResultPhotosFiles>>>.filterBySize( fun List<Pair<String, List<ResultDataFiles>>>.filterBySize(
minSize: Long, minSize: Long,
maxSize: Long maxSize: Long
): List<Pair<String, List<ResultPhotosFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
if (minSize == -1L) return this if (minSize == -1L) return this
return this.mapNotNull { (date, files) -> return this.mapNotNull { (date, files) ->
val filtered = files.filter { it.size in minSize..maxSize } val filtered = files.filter { it.size in minSize..maxSize }
@ -154,10 +154,10 @@ object ExtendFunctions {
/** /**
* 分组数据按大小筛选 ,音视频筛选时长 * 分组数据按大小筛选 ,音视频筛选时长
*/ */
fun List<Pair<String, List<ResultPhotosFiles>>>.filterByDuration( fun List<Pair<String, List<ResultDataFiles>>>.filterByDuration(
minSize: Long, minSize: Long,
maxSize: Long maxSize: Long
): List<Pair<String, List<ResultPhotosFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
if (minSize == -1L) return this if (minSize == -1L) return this
return this.mapNotNull { (date, files) -> return this.mapNotNull { (date, files) ->
val filtered = files.filter { it.duration in minSize..maxSize } val filtered = files.filter { it.duration in minSize..maxSize }
@ -181,7 +181,7 @@ object ExtendFunctions {
/** /**
* 移除掉缩略图后的数据 * 移除掉缩略图后的数据
*/ */
suspend fun List<Pair<String, List<ResultPhotosFiles>>>.filterThumbnailsAsync(): List<Pair<String, List<ResultPhotosFiles>>> = suspend fun List<Pair<String, List<ResultDataFiles>>>.filterThumbnailsAsync(): List<Pair<String, List<ResultDataFiles>>> =
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
this@filterThumbnailsAsync.asSequence() this@filterThumbnailsAsync.asSequence()
.mapNotNull { (key, files) -> .mapNotNull { (key, files) ->
@ -195,7 +195,7 @@ object ExtendFunctions {
/** /**
* 移除掉缩略图后的数据 * 移除掉缩略图后的数据
*/ */
suspend fun List<ResultPhotosFiles>.filterRemoveThumbnailsAsync(): List<ResultPhotosFiles> = suspend fun List<ResultDataFiles>.filterRemoveThumbnailsAsync(): List<ResultDataFiles> =
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
this@filterRemoveThumbnailsAsync.asSequence() this@filterRemoveThumbnailsAsync.asSequence()
.filter { !it.isThumbnail } // 去掉 isThumbnail = true 的项 .filter { !it.isThumbnail } // 去掉 isThumbnail = true 的项
@ -203,9 +203,9 @@ object ExtendFunctions {
} }
fun List<Pair<String, List<ResultPhotosFiles>>>.removeItem( fun List<Pair<String, List<ResultDataFiles>>>.removeItem(
target: ResultPhotosFiles target: ResultDataFiles
): List<Pair<String, List<ResultPhotosFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
return this.mapNotNull { (key, files) -> return this.mapNotNull { (key, files) ->
val updatedFiles = files.filterNot { it == target } val updatedFiles = files.filterNot { it == target }
if (updatedFiles.isNotEmpty()) key to updatedFiles else null if (updatedFiles.isNotEmpty()) key to updatedFiles else null

View File

@ -5,14 +5,11 @@ import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.text.format.Formatter
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi import com.ux.video.file.filerecovery.db.ResultData
import com.ux.video.file.filerecovery.photo.ResultPhotos import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
import com.ux.video.file.filerecovery.result.ScanningActivity
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents
@ -27,7 +24,6 @@ import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
@ -108,20 +104,21 @@ object ScanManager {
scanDocuments(root, depth = 0) scanDocuments(root, depth = 0)
val map = result.map { (dir, files) -> val map = result.map { (dir, files) ->
val resultPhotosFilesList = files.map { file -> val resultDataFilesList = files.map { file ->
ResultPhotosFiles( ResultDataFiles(
name = file.name, name = file.name,
path = file.absolutePath, path = file.absolutePath,
size = file.length(), size = file.length(),
sizeString = android.text.format.Formatter.formatFileSize( sizeString = Formatter.formatFileSize(
context, context,
file.length() file.length()
), ),
lastModified = file.lastModified(), lastModified = file.lastModified(),
resolution = getResolution(type,file) resolution = getResolution(type,file),
fileType = getFileType(type)
) )
} }
ResultPhotos(dir, ArrayList(resultPhotosFilesList)) ResultData(dir, ArrayList(resultDataFilesList))
} }
emit(ScanState.Complete(ArrayList(map))) emit(ScanState.Complete(ArrayList(map)))
} }
@ -168,7 +165,7 @@ object ScanManager {
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
@ -211,28 +208,40 @@ object ScanManager {
} }
} }
scanDir(root, depth = 0) scanDir(root, depth = 0)
ScanManager.showLog("HiddenScan", " 3333") ScanManager.showLog("HiddenScan", " 3333")
val map = result.map { (dir, files) -> val map = result.map { (dir, files) ->
val resultPhotosFilesList = files.map { file -> val resultDataFilesList = files.map { file ->
ResultPhotosFiles( ResultDataFiles(
name = file.name, name = file.name,
path = file.absolutePath, path = file.absolutePath,
size = file.length(), size = file.length(),
sizeString = android.text.format.Formatter.formatFileSize( sizeString = Formatter.formatFileSize(
context, context,
file.length() file.length()
), ),
lastModified = file.lastModified(), lastModified = file.lastModified(),
resolution = getResolution(type,file) resolution = getResolution(type,file),
fileType = getFileType(type)
) )
} }
ResultPhotos(dir, ArrayList(resultPhotosFilesList)) ResultData(dir, ArrayList(resultDataFilesList))
} }
emit(ScanState.Complete(ArrayList(map))) emit(ScanState.Complete(ArrayList(map)))
} }
private fun getFileType(scanType: Int): Int {
return when (scanType) {
VALUE_SCAN_TYPE_deleted_photo -> 0
VALUE_SCAN_TYPE_deleted_video -> 1
VALUE_SCAN_TYPE_deleted_audio -> 2
else -> 3
}
}
private fun getFileSizeByMediaStore(context: Context, file: File): Long { private fun getFileSizeByMediaStore(context: Context, file: File): Long {
val uri = Uri.fromFile(file) val uri = Uri.fromFile(file)
context.contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null) context.contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null)
@ -291,13 +300,14 @@ object ScanManager {
* @param folder "AllRecovery/Photo" * @param folder "AllRecovery/Photo"
*/ */
fun CoroutineScope.copySelectedFilesAsync( fun CoroutineScope.copySelectedFilesAsync(
selectedSet: Set<ResultPhotosFiles>, selectedSet: Set<ResultDataFiles>,
rootDir: File = Common.rootDir, rootDir: File = Common.rootDir,
folder: String, folder: String,
onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit, onProgress: (currentCounts: Int, data:ResultDataFiles,fileName: String, success: Boolean) -> Unit,
onComplete: (currentCounts: Int) -> Unit onComplete: (currentCounts: Int) -> Unit
) { ) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
var recoveryCount = 0
val targetDir = File(rootDir, folder) val targetDir = File(rootDir, folder)
if (!targetDir.exists()) targetDir.mkdirs() if (!targetDir.exists()) targetDir.mkdirs()
selectedSet.forEachIndexed { index, resultPhotosFiles -> selectedSet.forEachIndexed { index, resultPhotosFiles ->
@ -312,17 +322,19 @@ object ScanManager {
} }
} }
success = true success = true
recoveryCount++
withContext(Dispatchers.Main) {
onProgress(index + 1,resultPhotosFiles, srcFile.name, success)
}
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
withContext(Dispatchers.Main) {
onProgress(index + 1, srcFile.name, success)
}
} }
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
onComplete(selectedSet.size) onComplete(recoveryCount)
} }
} }
} }
@ -333,8 +345,8 @@ object ScanManager {
* *
*/ */
fun CoroutineScope.deleteFilesAsync( fun CoroutineScope.deleteFilesAsync(
selectedSet: Set<ResultPhotosFiles>, selectedSet: Set<ResultDataFiles>,
onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit, onProgress: (currentCounts: Int, data:ResultDataFiles,fileName: String, success: Boolean) -> Unit,
onComplete: (currentCounts: Int) -> Unit onComplete: (currentCounts: Int) -> Unit
) { ) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
@ -346,16 +358,20 @@ object ScanManager {
deletedCount++ deletedCount++
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
onProgress(index + 1, file.name, true) onProgress(index + 1, resultPhotosFiles,file.name, true)
} }
} catch (e: Exception) { } catch (e: Exception) {
onProgress(index + 1, resultPhotosFiles.path!!, false) onProgress(index + 1,resultPhotosFiles, resultPhotosFiles.path!!, false)
} }
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
onComplete(selectedSet.size) onComplete(deletedCount)
} }
} }
} }
} }

View File

@ -1,13 +1,9 @@
package com.ux.video.file.filerecovery.utils package com.ux.video.file.filerecovery.utils
import android.util.Log
import androidx.lifecycle.LiveData 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.db.ResultDataFiles
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class ScanRepository : ViewModel() { class ScanRepository : ViewModel() {
@ -20,17 +16,17 @@ class ScanRepository : ViewModel() {
private val _selectedLiveData = MutableLiveData<Set<ResultPhotosFiles>>(emptySet()) private val _selectedLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
val selectedLiveData: LiveData<Set<ResultPhotosFiles>> = _selectedLiveData val selectedLiveData: LiveData<Set<ResultDataFiles>> = _selectedLiveData
// 当前筛选显示的选中项 // 当前筛选显示的选中项
private val _selectedDisplayLiveData = MutableLiveData<Set<ResultPhotosFiles>>(emptySet()) private val _selectedDisplayLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
val selectedDisplayLiveData: LiveData<Set<ResultPhotosFiles>> = _selectedDisplayLiveData val selectedDisplayLiveData: LiveData<Set<ResultDataFiles>> = _selectedDisplayLiveData
fun toggleSelection(isAdd: Boolean, resultPhotosFiles: ResultPhotosFiles) { fun toggleSelection(isAdd: Boolean, resultDataFiles: ResultDataFiles) {
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 { resultDataFiles.let {
if (isAdd) { if (isAdd) {
current.add(it) current.add(it)
currentDisplay.add(it) currentDisplay.add(it)
@ -52,7 +48,7 @@ class ScanRepository : ViewModel() {
/** /**
* 数据筛选后或者缩略图显示开关切换后 重置当前显示的选中集合 * 数据筛选后或者缩略图显示开关切换后 重置当前显示的选中集合
*/ */
fun filterResetDisplayFlow(list: MutableSet<ResultPhotosFiles>){ fun filterResetDisplayFlow(list: MutableSet<ResultDataFiles>){
_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} ")
@ -75,7 +71,7 @@ class ScanRepository : ViewModel() {
/** /**
* 详情页删除完毕移除删除掉的数据 * 详情页删除完毕移除删除掉的数据
*/ */
fun afterSingleDeleted(deletedItem:ResultPhotosFiles){ fun afterSingleDeleted(deletedItem:ResultDataFiles){
val selected = _selectedLiveData.value.orEmpty().toMutableSet() val selected = _selectedLiveData.value.orEmpty().toMutableSet()
val display = _selectedDisplayLiveData.value.orEmpty().toMutableSet() val display = _selectedDisplayLiveData.value.orEmpty().toMutableSet()
@ -88,9 +84,9 @@ class ScanRepository : ViewModel() {
fun checkIsSelect(resultPhotosFiles: ResultPhotosFiles): Boolean { fun checkIsSelect(resultDataFiles: ResultDataFiles): Boolean {
val current = _selectedLiveData.value val current = _selectedLiveData.value
return current?.contains(resultPhotosFiles) == true return current?.contains(resultDataFiles) == true
} }

View File

@ -1,9 +1,9 @@
package com.ux.video.file.filerecovery.utils package com.ux.video.file.filerecovery.utils
import com.ux.video.file.filerecovery.photo.ResultPhotos import com.ux.video.file.filerecovery.db.ResultData
sealed class ScanState { sealed class ScanState {
data class Progress(val scannedCount: Int,val filePath: String) : ScanState() data class Progress(val scannedCount: Int,val filePath: String) : ScanState()
data class Complete(val result: ArrayList<ResultPhotos>) : ScanState() data class Complete(val result: ArrayList<ResultData>) : ScanState()
} }

View File

@ -3,27 +3,18 @@ package com.ux.video.file.filerecovery.video
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.SeekBar
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityScanSelectTypeBinding
import com.ux.video.file.filerecovery.databinding.ActivityVideoPlayBinding import com.ux.video.file.filerecovery.databinding.ActivityVideoPlayBinding
import com.ux.video.file.filerecovery.photo.PhotoInfoActivity import com.ux.video.file.filerecovery.sort.RecoverOrDeleteManager
import com.ux.video.file.filerecovery.photo.PhotoInfoActivity.Companion.KEY_CLICK_ITEM import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.photo.RecoverOrDeleteManager
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
import com.ux.video.file.filerecovery.success.RecoverySuccessActivity import com.ux.video.file.filerecovery.success.RecoverySuccessActivity
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
@ -34,7 +25,7 @@ class VideoPlayActivity : BaseActivity<ActivityVideoPlayBinding>() {
} }
private lateinit var player: ExoPlayer private lateinit var player: ExoPlayer
private var myData: ResultPhotosFiles? = null private var myData: ResultDataFiles? = null
private val updateHandler = Handler(Looper.getMainLooper()) private val updateHandler = Handler(Looper.getMainLooper())
override fun inflateBinding(inflater: LayoutInflater): ActivityVideoPlayBinding = override fun inflateBinding(inflater: LayoutInflater): ActivityVideoPlayBinding =
ActivityVideoPlayBinding.inflate(inflater) ActivityVideoPlayBinding.inflate(inflater)
@ -48,7 +39,7 @@ class VideoPlayActivity : BaseActivity<ActivityVideoPlayBinding>() {
override fun initData() { override fun initData() {
super.initData() super.initData()
myData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { myData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(KEY_DATA, ResultPhotosFiles::class.java) intent.getParcelableExtra(KEY_DATA, ResultDataFiles::class.java)
} else { } else {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
intent.getParcelableExtra(KEY_DATA) intent.getParcelableExtra(KEY_DATA)

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/main_title" android:state_selected="true"/>
<item android:color="@color/main_sub_title" android:state_selected="false"/>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -7,7 +7,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/white" android:background="@color/white"
android:orientation="vertical" android:orientation="vertical"
tools:context=".photo.PhotoInfoActivity"> tools:context=".sort.PhotoInfoActivity">
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -7,7 +7,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/white" android:background="@color/white"
android:orientation="vertical" android:orientation="vertical"
tools:context=".photo.PhotoSortingActivity"> tools:context=".sort.PhotoSortingActivity">
<RelativeLayout <RelativeLayout
android:id="@+id/layout_top" android:id="@+id/layout_top"

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".recovery.RecoveryActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="44dp"
android:background="@color/white"
android:gravity="center_vertical">
<ImageView
android:id="@+id/image_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingHorizontal="13dp"
android:paddingVertical="14dp"
android:src="@drawable/black_return" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/recovered_files"
android:textColor="@color/main_title"
android:textSize="16sp"
app:fontType="bold" />
</RelativeLayout>
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="66dp"
app:tabIndicatorHeight="4dp"
app:tabMaxWidth="18dp"
app:tabMode="fixed"
app:tabGravity="fill"
android:id="@+id/tab_layout"
app:tabIndicatorColor="@color/color_title_blue"
app:tabIndicatorGravity="center"
app:tabBackground="@color/white"/>
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:id="@+id/view_page2"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recovery_photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.recoveryphoto.RecoveryPhotoFragment">
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RecoveryPhotoFragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="64dp">
<ImageView
android:id="@+id/image_select"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/selector_icon_checkmark_28dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:id="@+id/image_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:src="@drawable/icon_apk"
app:layout_constraintStart_toEndOf="@id/image_select" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:gravity="bottom"
android:maxLines="1"
android:textColor="@color/main_title"
android:textSize="14sp"
app:fontType="bold"
app:layout_constraintBottom_toTopOf="@id/linear_duration"
app:layout_constraintLeft_toRightOf="@id/image_icon"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="aaaaaaaaaassssssssssssssssssssssssssssssssa" />
<LinearLayout
android:id="@+id/linear_duration"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="5dp"
android:gravity="top"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="@id/text_name"
app:layout_constraintTop_toBottomOf="@id/text_name">
<com.ux.video.file.filerecovery.utils.CustomTextView
android:id="@+id/text_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/main_sub_title"
android:textSize="11sp"
tools:text="aaaaaaaaaaa" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:id="@+id/text_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textColor="@color/main_sub_title"
android:textSize="11sp"
tools:text="aaaaaaaaaaa" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.ux.video.file.filerecovery.utils.CustomTextView
android:id="@+id/tab_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/selector_recovery_file_tab_layout_title"
android:textSize="16sp"
app:fontType="bold" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:id="@+id/tab_item_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/main_sub_title"
android:textSize="14sp"
app:fontType="bold" />
</LinearLayout>

View File

@ -86,6 +86,8 @@ wait..</string>
<string name="view">View</string> <string name="view">View</string>
<string name="not_found">SorryNo %s found</string> <string name="not_found">SorryNo %s found</string>
<string name="search">Search</string> <string name="search">Search</string>
<string name="recovered_files">Recovered files</string>
<string name="text_counts">(%d)</string>
<string-array name="filter_date"> <string-array name="filter_date">

View File

@ -3,3 +3,9 @@ plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.android) apply false
} }
buildscript {
dependencies {
classpath("io.objectbox:objectbox-gradle-plugin:4.0.3")
}
}

View File

@ -10,6 +10,9 @@ appcompat = "1.7.1"
material = "1.12.0" material = "1.12.0"
activity = "1.10.1" activity = "1.10.1"
constraintlayout = "2.2.1" constraintlayout = "2.2.1"
lifecycleLivedataKtx = "2.9.4"
lifecycleViewmodelKtx = "2.9.4"
fragmentKtx = "1.8.9"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -22,6 +25,9 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }