diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 67dfffd..3545135 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,6 +3,7 @@ plugins { alias(libs.plugins.kotlin.android) id ("kotlin-kapt") id ("kotlin-parcelize") + id("io.objectbox") } android { @@ -47,6 +48,9 @@ dependencies { implementation(libs.material) implementation(libs.androidx.activity) implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.lifecycle.livedata.ktx) + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(libs.androidx.fragment.ktx) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/objectbox-models/default.json b/app/objectbox-models/default.json new file mode 100644 index 0000000..6e25a4d --- /dev/null +++ b/app/objectbox-models/default.json @@ -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 +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 13ce729..aea2e06 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,12 +2,8 @@ - - + + @@ -24,14 +20,17 @@ android:supportsRtl="true" android:theme="@style/Theme.FileRecovery" tools:targetApi="31"> + : 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/db/ObjectBoxManager.kt b/app/src/main/java/com/ux/video/file/filerecovery/db/ObjectBoxManager.kt new file mode 100644 index 0000000..6aadeb4 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/db/ObjectBoxManager.kt @@ -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? = 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 { + AllRecoveryBox = + AllRecoveryBox ?: objectboxStore.boxFor(ResultDataFiles::class.java) + + return AllRecoveryBox!! + } + + // fun getLikeBox(): Box? { +// likeWallpaperBox = likeWallpaperBox ?: boxStore?.boxFor(ResultPhotosFiles::class.java) +// return likeWallpaperBox +// } +// +// fun setLikeUpdateListener(queryAllLike:(List)->Unit): DataSubscription { +// val likeBox: Box? = getLikeBox() +// val build: Query = 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 = 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? = 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? = getLikeBox() +// val first: LikeWallpaper? = likeBox!!.query() +// .equal(LikeWallpaper_.id, id) +// .build() +// .findFirst() +// return first != null +// } +// +// +// + fun queryRecoveryFile(type: Int): List { + val dataBox = getDataBox() + val list: List = dataBox.query(ResultDataFiles_.fileType.equal(type)) + .build() + .find() + + return list + } + + suspend fun queryRecoveryFileAsync(type: Int): List = + withContext(Dispatchers.IO) { + val dataBox = getDataBox() + dataBox.query(ResultDataFiles_.fileType.equal(type)) + .build() + .find() + } + +// +// fun queryLike(currentPage: Int): List { +// val offset: Int = (currentPage - 1) * pageSize +// val likeBox: Box? = getLikeBox() +// val data: List = likeBox!!.query() +// .build() +// .find(offset.toLong(), pageSize.toLong()) +// +// return data +// } +// +// fun queryAllData(wallpaperType: Long): List { +// val objectBoxLike: Box? = getDataBox() +// val data: List = objectBoxLike!!.query() +// .equal(VideoWallpaper_.wallpapertype, wallpaperType) +// .build() +// .find() +// +// return data +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotos.kt b/app/src/main/java/com/ux/video/file/filerecovery/db/ResultData.kt similarity index 50% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotos.kt rename to app/src/main/java/com/ux/video/file/filerecovery/db/ResultData.kt index 001e03e..ba4f55b 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotos.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/db/ResultData.kt @@ -1,11 +1,11 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.db import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class ResultPhotos( +data class ResultData( val dirName: String, - val allFiles: ArrayList + val allFiles: ArrayList ): Parcelable diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotosFiles.kt b/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFiles.kt similarity index 77% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotosFiles.kt rename to app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFiles.kt index 9e7ee9c..0aebcde 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotosFiles.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFiles.kt @@ -1,15 +1,22 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.db import java.io.File import android.os.Parcelable import com.ux.video.file.filerecovery.utils.Common +import io.objectbox.annotation.Entity +import io.objectbox.annotation.Id import kotlinx.parcelize.Parcelize +@Entity @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 path: String? = null, + val path: String, val size: Long, // 字节 val sizeString: String, val lastModified: Long, // 时间戳 @@ -36,4 +43,5 @@ data class ResultPhotosFiles( val duration: Long get() { return Common.getMediaDuration(path.toString()) } + } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultAdapter.kt index 9128a53..40773b7 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultAdapter.kt @@ -5,10 +5,10 @@ import android.view.LayoutInflater import android.view.ViewGroup import com.ux.video.file.filerecovery.base.BaseAdapter 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) : - BaseAdapter(mContext) { + BaseAdapter(mContext) { override fun getViewBinding(parent: ViewGroup): DocumentsScanResultAdapterBinding = DocumentsScanResultAdapterBinding.inflate( LayoutInflater.from(parent.context), @@ -18,7 +18,7 @@ class DocumentsScanResultAdapter(mContext: Context) : override fun bindItem( holder: VHolder, - item: ResultPhotos + item: ResultData ) { holder.vb.run { diff --git a/app/src/main/java/com/ux/video/file/filerecovery/main/MainActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/main/MainActivity.kt index c598897..de8567c 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/main/MainActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/main/MainActivity.kt @@ -1,7 +1,6 @@ package com.ux.video.file.filerecovery.main import android.Manifest -import android.app.Activity import android.content.Context import android.content.Intent 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.base.BaseActivity import com.ux.video.file.filerecovery.databinding.ActivityMainBinding -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity -import com.ux.video.file.filerecovery.photo.DateFilterPopupWindows -import com.ux.video.file.filerecovery.photo.DatePickerDialogFragment +import com.ux.video.file.filerecovery.recovery.RecoveryActivity +import com.ux.video.file.filerecovery.sort.DatePickerDialogFragment import com.ux.video.file.filerecovery.utils.ScanManager class MainActivity : BaseActivity() { @@ -95,6 +93,9 @@ class MainActivity : BaseActivity() { currentGoType = ScanSelectTypeActivity.Companion.VALUE_DOCUMENT intentCheck() } + layoutRecovery.setOnClickListener { + startActivity(Intent(this@MainActivity,RecoveryActivity::class.java)) + } } binding.tvTitle.setOnClickListener { diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/RecoveryActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/RecoveryActivity.kt new file mode 100644 index 0000000..738a14f --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/recovery/RecoveryActivity.kt @@ -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() { + 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(R.id.tab_item_title) + val tvCount = tabView.findViewById(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(R.id.tab_item_count)?.text = getString(R.string.text_counts,recoveryFilePhoto.size) + getTabAt(1)?.customView?.findViewById(R.id.tab_item_count)?.text = getString(R.string.text_counts,recoveryFileVideo.size) + getTabAt(2)?.customView?.findViewById(R.id.tab_item_count)?.text = getString(R.string.text_counts,recoveryFileAudio.size) + getTabAt(3)?.customView?.findViewById(R.id.tab_item_count)?.text = getString(R.string.text_counts,recoveryFileDocuments.size) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/MyViewPage2Adapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/MyViewPage2Adapter.kt new file mode 100644 index 0000000..d004dff --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/MyViewPage2Adapter.kt @@ -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 +) : FragmentStateAdapter(fragmentActivity) { + override fun getItemCount(): Int = fragments.size + override fun createFragment(position: Int): Fragment = fragments[position] +} diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoFragment.kt new file mode 100644 index 0000000..c1bb2c8 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoFragment.kt @@ -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() { + + 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) + + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoViewModel.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoViewModel.kt new file mode 100644 index 0000000..8c76495 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoViewModel.kt @@ -0,0 +1,7 @@ +package com.ux.video.file.filerecovery.recovery.ui.recoveryphoto + +import androidx.lifecycle.ViewModel + +class RecoveryPhotoViewModel : ViewModel() { + +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt index 3537692..48f83bf 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt @@ -7,9 +7,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.databinding.ActivityScanResultDisplayBinding -import com.ux.video.file.filerecovery.photo.PhotoSortingActivity -import com.ux.video.file.filerecovery.photo.ResultPhotos -import com.ux.video.file.filerecovery.photo.ResultPhotosFiles +import com.ux.video.file.filerecovery.sort.PhotoSortingActivity +import com.ux.video.file.filerecovery.db.ResultData +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.VALUE_SCAN_TYPE_audio import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio @@ -28,7 +28,7 @@ class ScanResultDisplayActivity : BaseActivity private var scanType: Int = VALUE_SCAN_TYPE_photo private var exitDialog: ExitDialogFragment? = null - private var list: ArrayList? = null + private var list: ArrayList? = null companion object { val KEY_SCAN_RESULT = "scan_result" @@ -136,7 +136,7 @@ class ScanResultDisplayActivity : BaseActivity } - private fun goSort(list: ArrayList) { + private fun goSort(list: ArrayList) { startActivity( Intent( this@ScanResultDisplayActivity, diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAdapter.kt index 73d146b..e24d83e 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAdapter.kt @@ -4,20 +4,12 @@ import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater 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.base.BaseAdapter -import com.ux.video.file.filerecovery.databinding.ScanResultAdapterBinding import com.ux.video.file.filerecovery.databinding.ScanResultDocumentsAdapterBinding -import com.ux.video.file.filerecovery.photo.ResultPhotos -import com.ux.video.file.filerecovery.photo.ResultPhotosFiles +import com.ux.video.file.filerecovery.db.ResultData +import com.ux.video.file.filerecovery.db.ResultDataFiles 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( mContext: Context, var type: Int, - var onClickItem: (allFiles: ArrayList) -> Unit + var onClickItem: (allFiles: ArrayList) -> Unit ) : - BaseAdapter(mContext) { + BaseAdapter(mContext) { override fun getViewBinding(parent: ViewGroup): ScanResultDocumentsAdapterBinding = ScanResultDocumentsAdapterBinding.inflate( LayoutInflater.from(parent.context), @@ -38,7 +30,7 @@ class ScanResultDocumentsAdapter( @SuppressLint("SetTextI18n") override fun bindItem( holder: VHolder, - item: ResultPhotos + item: ResultData ) { holder.vb.run { diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoAdapter.kt index 6f6adc5..9619eaf 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoAdapter.kt @@ -11,17 +11,17 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestOptions import com.ux.video.file.filerecovery.base.BaseAdapter import com.ux.video.file.filerecovery.databinding.ScanResultAdapterBinding -import com.ux.video.file.filerecovery.photo.ResultPhotos -import com.ux.video.file.filerecovery.photo.ResultPhotosFiles +import com.ux.video.file.filerecovery.db.ResultData +import com.ux.video.file.filerecovery.db.ResultDataFiles import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import java.io.File class ScanResultPhotoAdapter( mContext: Context, var type: Int, - var onClickItem: (allFiles: ArrayList) -> Unit + var onClickItem: (allFiles: ArrayList) -> Unit ) : - BaseAdapter(mContext) { + BaseAdapter(mContext) { override fun getViewBinding(parent: ViewGroup): ScanResultAdapterBinding = ScanResultAdapterBinding.inflate( LayoutInflater.from(parent.context), @@ -32,7 +32,7 @@ class ScanResultPhotoAdapter( @SuppressLint("SetTextI18n") override fun bindItem( holder: VHolder, - item: ResultPhotos + item: ResultData ) { holder.vb.run { diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/ConfirmDeleteDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/ConfirmDeleteDialogFragment.kt similarity index 96% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/ConfirmDeleteDialogFragment.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/ConfirmDeleteDialogFragment.kt index ef0dcc7..c5ba3cf 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/ConfirmDeleteDialogFragment.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/ConfirmDeleteDialogFragment.kt @@ -1,4 +1,4 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.graphics.Color import android.os.Bundle diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/DateFilterPopupWindows.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/DateFilterPopupWindows.kt similarity index 99% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/DateFilterPopupWindows.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/DateFilterPopupWindows.kt index 75cbb9d..b9fb7df 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/DateFilterPopupWindows.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/DateFilterPopupWindows.kt @@ -1,4 +1,4 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.content.Context diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/DatePickerDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/DatePickerDialogFragment.kt similarity index 99% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/DatePickerDialogFragment.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/DatePickerDialogFragment.kt index 29f9742..4559edf 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/DatePickerDialogFragment.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/DatePickerDialogFragment.kt @@ -1,4 +1,4 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.annotation.SuppressLint import android.content.Context diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/DeletingDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/DeletingDialogFragment.kt similarity index 93% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/DeletingDialogFragment.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/DeletingDialogFragment.kt index 82d3ee6..71c42c3 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/DeletingDialogFragment.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/DeletingDialogFragment.kt @@ -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.base.BaseIngDialogFragment diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/FilterPopupWindows.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/FilterPopupWindows.kt similarity index 98% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/FilterPopupWindows.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/FilterPopupWindows.kt index f69fa5e..11d1e6a 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/FilterPopupWindows.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/FilterPopupWindows.kt @@ -1,4 +1,4 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.content.Context import android.graphics.Color @@ -9,7 +9,6 @@ import android.view.View import android.view.ViewGroup import android.widget.PopupWindow import android.widget.RelativeLayout -import androidx.core.view.forEach import com.ux.video.file.filerecovery.databinding.CommonLayoutFilterItemBinding import com.ux.video.file.filerecovery.databinding.PopwindowsFilterBinding import com.ux.video.file.filerecovery.utils.Common diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateAdapter.kt similarity index 81% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateAdapter.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateAdapter.kt index cb82f29..15ca6ea 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateAdapter.kt @@ -1,18 +1,18 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup -import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager 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.db.ResultDataFiles 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 class PhotoDisplayDateAdapter( @@ -20,10 +20,10 @@ class PhotoDisplayDateAdapter( var scanType: Int, var mColumns: Int, var viewModel: ScanRepository, - var onSelectedUpdate: (resultPhotosFiles: ResultPhotosFiles, isAdd: Boolean) -> Unit, - var clickItem: (item: ResultPhotosFiles) -> Unit + var onSelectedUpdate: (resultDataFiles: ResultDataFiles, isAdd: Boolean) -> Unit, + var clickItem: (item: ResultDataFiles) -> Unit ) : - BaseAdapter>, PhotoDisplayDateAdapterBinding>(mContext) { + BaseAdapter>, PhotoDisplayDateAdapterBinding>(mContext) { private var allSelected: Boolean? = null @@ -75,7 +75,7 @@ class PhotoDisplayDateAdapter( @SuppressLint("SetTextI18n") override fun bindItem( holder: VHolder, - item: Pair> + item: Pair> ) { holder.vb.run { item.run { @@ -109,9 +109,10 @@ class PhotoDisplayDateAdapter( recyclerChild.apply { 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) } + else -> { GridLayoutManager(context, mColumns) } @@ -125,17 +126,17 @@ class PhotoDisplayDateAdapter( } } - object ItemDiffCallback : DiffUtil.ItemCallback>>() { + object ItemDiffCallback : DiffUtil.ItemCallback>>() { override fun areItemsTheSame( - oldItem: Pair>, - newItem: Pair> + oldItem: Pair>, + newItem: Pair> ): Boolean { return oldItem.first == newItem.first } override fun areContentsTheSame( - oldItem: Pair>, - newItem: Pair> + oldItem: Pair>, + newItem: Pair> ): Boolean { return oldItem == newItem } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateChildAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateChildAdapter.kt similarity index 65% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateChildAdapter.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateChildAdapter.kt index b703131..bf3b4fe 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateChildAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateChildAdapter.kt @@ -1,4 +1,4 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.content.Context import android.graphics.drawable.Drawable @@ -7,7 +7,6 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.RelativeLayout -import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide 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.databinding.FileSpanCountThreeAdapterBinding 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.CustomTextView import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx @@ -40,23 +41,25 @@ class PhotoDisplayDateChildAdapter( * @param addOrRemove 选中还是取消选中 * @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 clickItem: (item: ResultPhotosFiles) -> Unit + var clickItem: (item: ResultDataFiles) -> Unit ) : - NewBaseAdapter(mContext) { + NewBaseAdapter(mContext) { //日期组某一天的数据选择状态维护 - val dateSelectedMap = mutableSetOf() + val dateSelectedMap = mutableSetOf() companion object { - //音频或者文档 - private const val TYPE_ONE = 1 + //视频和图片支持布局切换 2/3/4列 private const val TYPE_TWO = 2 private const val TYPE_THREE = 3 private const val TYPE_FOUR = 4 + + private const val TYPE_AUDIO = 5 + private const val TYPE_DOCUMENTS = 6 } fun setAllSelected(isAdd: Boolean) { @@ -69,7 +72,11 @@ class PhotoDisplayDateChildAdapter( override fun getItemViewType(position: Int): Int { when (scanType) { 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 -> { @@ -110,8 +117,16 @@ class PhotoDisplayDateChildAdapter( ): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) return when (viewType) { - TYPE_ONE -> OneHolder( - OneAudioDocumentsItemBinding.inflate( + TYPE_AUDIO -> AudioHolder( + OneAudioItemBinding.inflate( + inflater, + parent, + false + ) + ) + + TYPE_DOCUMENTS -> DocumentsHolder( + OneDocumentsItemBinding.inflate( inflater, parent, false @@ -139,32 +154,36 @@ class PhotoDisplayDateChildAdapter( override fun onBind( holder: RecyclerView.ViewHolder, - item: ResultPhotosFiles, + item: ResultDataFiles, viewType: Int ) { when (holder) { is TwoHolder -> holder.vb.run { initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item, imageType) } + is ThreeHolder -> holder.vb.run { initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item, imageType) } - is OneHolder -> { + + is AudioHolder -> { item.run { holder.vb.let { it.textName.text = name it.textDuration.text = Common.formatDuration(duration) it.textSize.text = sizeString - viewModel.checkIsSelect(this).let { isSelected -> - it.imageSelect.isSelected = isSelected - addOrRemove(this, isSelected) - } - it.imageSelect.setOnClickListener { - it.isSelected = !it.isSelected - it.isSelected.let { newStatus -> - addOrRemove(this, newStatus) - } - } + + initAudioDocuments(it.imageSelect,this) +// viewModel.checkIsSelect(this).let { isSelected -> +// it.imageSelect.isSelected = isSelected +// addOrRemove(this, isSelected) +// } +// it.imageSelect.setOnClickListener { +// it.isSelected = !it.isSelected +// it.isSelected.let { newStatus -> +// addOrRemove(this, newStatus) +// } +// } it.constraintLayout.setOnClickListener { 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) : RecyclerView.ViewHolder(vb.root) - class OneHolder(val vb: OneAudioDocumentsItemBinding) : + class AudioHolder(val vb: OneAudioItemBinding) : 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( rootLayout: RelativeLayout, imageSelectStatus: ImageView, textSize: CustomTextView, imageThumbnail: ImageView, - item: ResultPhotosFiles, + item: ResultDataFiles, imageType: ImageView ) { item.run { - - viewModel.checkIsSelect(this).let { - imageSelectStatus.isSelected = it - addOrRemove(this, it) - } - imageSelectStatus.setOnClickListener { - it.isSelected = !it.isSelected - it.isSelected.let { newStatus -> - addOrRemove(this, newStatus) - } - } + initAudioDocuments(imageSelectStatus,this) +// viewModel.checkIsSelect(this).let { +// imageSelectStatus.isSelected = it +// addOrRemove(this, it) +// } +// imageSelectStatus.setOnClickListener { +// it.isSelected = !it.isSelected +// it.isSelected.let { newStatus -> +// addOrRemove(this, newStatus) +// } +// } textSize.text = sizeString imageType.setImageResource( @@ -254,13 +311,13 @@ class PhotoDisplayDateChildAdapter( } - private fun addOrRemove(resultPhotosFiles: ResultPhotosFiles, boolean: Boolean) { + private fun addOrRemove(resultDataFiles: ResultDataFiles, boolean: Boolean) { if (boolean) { - dateSelectedMap.add(resultPhotosFiles) + dateSelectedMap.add(resultDataFiles) } else { - dateSelectedMap.remove(resultPhotosFiles) + dateSelectedMap.remove(resultDataFiles) } - onSelectedUpdate.invoke(resultPhotosFiles, boolean, dateSelectedMap.size == data.size) + onSelectedUpdate.invoke(resultDataFiles, boolean, dateSelectedMap.size == data.size) } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoInfoActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoInfoActivity.kt similarity index 87% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoInfoActivity.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoInfoActivity.kt index 96f46eb..8dc9506 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoInfoActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoInfoActivity.kt @@ -1,17 +1,13 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.content.Intent import android.graphics.drawable.Drawable -import android.net.Uri import android.os.Build import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView import androidx.core.view.isVisible 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.load.DataSource 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.base.BaseActivity 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.utils.Common import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE @@ -46,15 +43,15 @@ class PhotoInfoActivity : BaseActivity() { } private var scanType: Int = VALUE_SCAN_TYPE_photo - private var myData: ResultPhotosFiles? = null - private lateinit var player: ExoPlayer + private var myData: ResultDataFiles? = null + override fun inflateBinding(inflater: LayoutInflater): ActivityPhotoInfoBinding = ActivityPhotoInfoBinding.inflate(inflater) override fun initView() { super.initView() 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 { @Suppress("DEPRECATION") intent.getParcelableExtra(KEY_CLICK_ITEM) @@ -77,6 +74,9 @@ class PhotoInfoActivity : BaseActivity() { tvDate.text = Common.getFormatDate(resultPhotosFiles.lastModified) tvResolution.text = resultPhotosFiles.resolution tvDuration.text = Common.formatDuration(resultPhotosFiles.duration) + resultPhotosFiles.targetFile?.let { + tvType.text = Common.getMimeTypeParts(it) + } layoutBottom.tvLeft.run { text = resources.getString(R.string.delete) setOnClickListener { @@ -161,14 +161,8 @@ class PhotoInfoActivity : BaseActivity() { layoutSeekbar.isVisible = true imPlay.isVisible = true frameImage.setBackgroundResource(R.drawable.bg_info_music_f2f2f7_8) - image.setImageResource(R.drawable.image_info_music) - 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 + + loadCenterImage(image,R.drawable.image_info_music) initPlayAudio() layoutResolution.isVisible = false @@ -182,15 +176,29 @@ class PhotoInfoActivity : BaseActivity() { layoutSize.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 } } - } - } + 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){ Glide.with(this@PhotoInfoActivity) .load(file) diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoSortingActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoSortingActivity.kt similarity index 96% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoSortingActivity.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoSortingActivity.kt index 9e27c9a..60eee68 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoSortingActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoSortingActivity.kt @@ -1,4 +1,4 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.content.Intent 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.base.BaseActivity 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.utils.Common import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE @@ -97,21 +98,21 @@ class PhotoSortingActivity : BaseActivity() { private var filterLayoutPopupWindows: FilterPopupWindows? = null - private lateinit var sortBySizeBigToSmall: List - private lateinit var sortBySizeSmallToBig: List - private lateinit var sortByDateReverse: List>> - private lateinit var sortedByDatePositive: List>> + private lateinit var sortBySizeBigToSmall: List + private lateinit var sortBySizeSmallToBig: List + private lateinit var sortByDateReverse: List>> + private lateinit var sortedByDatePositive: List>> //最新显示的数据集合(包含缩略图 ,只保存当前筛选后或者排序后显示的数据,不受switch切换影响) - private var currentDateList: List>>? = null - private var currentSizeList: List? = null + private var currentDateList: List>>? = null + private var currentSizeList: List? = null //选中的所有数据集合(实际选中) - private lateinit var allSelectedSetList: Set + private lateinit var allSelectedSetList: Set //选中的所有数据集合(筛选后的数据实际显示的所有选中) - private lateinit var filterSelectedSetList: Set + private lateinit var filterSelectedSetList: Set private lateinit var mItemDecoration: GridSpacingItemDecoration @@ -125,7 +126,7 @@ class PhotoSortingActivity : BaseActivity() { override fun initData() { super.initData() scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) - val list: ArrayList? = + val list: ArrayList? = intent.getParcelableArrayListExtraCompat(KEY_PHOTO_FOLDER_FILE) mItemDecoration = GridSpacingItemDecoration(columns, Common.itemSpacing, Common.horizontalSpacing) @@ -338,7 +339,7 @@ class PhotoSortingActivity : BaseActivity() { is PhotoDisplayDateAdapter -> { dateAdapter?.setAllSelected(it.isSelected) dateAdapter?.getCurrentData()?.let { - it as List>> + it as List>> if (it.size > 0) Common.showLog("------------全选按钮 日期-${it.size} ${it[0].second[0].path}") } @@ -348,7 +349,7 @@ class PhotoSortingActivity : BaseActivity() { is PhotoDisplayDateChildAdapter -> { sizeSortAdapter?.setAllSelected(it.isSelected) sizeSortAdapter?.getCurrentData()?.let { - it as List + it as List if (it.size > 0) Common.showLog("------------全选按钮 大小-${it.size} ${it[0].path}") } @@ -437,12 +438,12 @@ class PhotoSortingActivity : BaseActivity() { } - private fun initGetCurrentSizeList(): List { + private fun initGetCurrentSizeList(): List { currentSizeList = currentSizeList ?: currentDateList?.flatMap { it.second } return currentSizeList!! } - private fun resetCurrentSizeList(currentList: List) { + private fun resetCurrentSizeList(currentList: List) { currentSizeList = currentList currentDateList = null @@ -451,12 +452,12 @@ class PhotoSortingActivity : BaseActivity() { } - private fun initGetCurrentDateList(): List>> { + private fun initGetCurrentDateList(): List>> { currentDateList = currentDateList ?: Common.getSortByDayNewToOldInit(currentSizeList!!) return currentDateList!! } - private fun resetCurrentDateList(currentList: List>>) { + private fun resetCurrentDateList(currentList: List>>) { currentDateList = currentList currentSizeList = null val totalSelectedCount = currentList.sumOf { pair -> @@ -484,7 +485,7 @@ class PhotoSortingActivity : BaseActivity() { val aPx = 16.dpToPx(context) val bottom = 70.dpToPx(context) 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) setPadding(aPx, 0, 0, bottom) } @@ -713,8 +714,8 @@ class PhotoSortingActivity : BaseActivity() { * 数据筛选或者缩略图切换显示后,对比刷新实际显示的选中数据 */ private fun checkRefreshDisPlaySelected( - list1: List>>? = null, - list2: List? = null + list1: List>>? = null, + list2: List? = null ) { lifecycleScope.launch { list1?.let { @@ -887,13 +888,13 @@ class PhotoSortingActivity : BaseActivity() { //选中集合的更新 viewModel.afterDeleted() deferredResults["sizeList"]?.let { list -> - list as List + list as List Common.showLog("---------更新 sizeList = ${list.size}") sizeSortAdapter?.setData(list) resetCurrentSizeList(list) } deferredResults["dateList"]?.let { list -> - list as List>> + list as List>> Common.showLog("---------更新 dateList = ${list.size}") dateAdapter?.setData(list) resetCurrentDateList(list) @@ -932,13 +933,13 @@ class PhotoSortingActivity : BaseActivity() { //选中集合的更新 viewModel.afterSingleDeleted(deletedData) deferredResults["sizeList"]?.let { list -> - list as List + list as List Common.showLog("---------更新 sizeList = ${list.size}") sizeSortAdapter?.setData(list) resetCurrentSizeList(list) } deferredResults["dateList"]?.let { list -> - list as List>> + list as List>> Common.showLog("---------更新 dateList = ${list.size}") dateAdapter?.setData(list) resetCurrentDateList(list) diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/RecoverOrDeleteManager.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/RecoverOrDeleteManager.kt similarity index 71% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/RecoverOrDeleteManager.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/RecoverOrDeleteManager.kt index 579f545..b1873cf 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/RecoverOrDeleteManager.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/RecoverOrDeleteManager.kt @@ -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 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.ScanManager import com.ux.video.file.filerecovery.utils.ScanManager.copySelectedFilesAsync @@ -18,9 +18,9 @@ object RecoverOrDeleteManager { 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 } @@ -31,21 +31,10 @@ object RecoverOrDeleteManager { fun showRecoveringDialog( fragmentManager: FragmentManager, scope: CoroutineScope, - selectedSetList: Set, + selectedSetList: Set, 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?.run { total = selectedSetList.size @@ -55,6 +44,21 @@ object RecoverOrDeleteManager { } 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, fragmentManager: FragmentManager, scope: CoroutineScope, - selectedSetList: Set, + selectedSetList: Set, onComplete: (number: Int) -> Unit ) { dialogConfirmDelete = dialogConfirmDelete ?: ConfirmDeleteDialogFragment() @@ -86,18 +90,10 @@ object RecoverOrDeleteManager { isInfoDelete: Boolean = false, fragmentManager: FragmentManager, scope: CoroutineScope, - selectedSetList: Set, + selectedSetList: Set, 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?.run { total = selectedSetList.size @@ -110,5 +106,18 @@ object RecoverOrDeleteManager { } 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}") + } } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/RecoveringDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/RecoveringDialogFragment.kt similarity index 93% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/RecoveringDialogFragment.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/RecoveringDialogFragment.kt index 1a7ea4f..1b8a1ba 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/RecoveringDialogFragment.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/RecoveringDialogFragment.kt @@ -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.base.BaseIngDialogFragment diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/SortDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDialogFragment.kt similarity index 98% rename from app/src/main/java/com/ux/video/file/filerecovery/photo/SortDialogFragment.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/SortDialogFragment.kt index 14dabe3..72f5009 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/SortDialogFragment.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDialogFragment.kt @@ -1,4 +1,4 @@ -package com.ux.video.file.filerecovery.photo +package com.ux.video.file.filerecovery.sort import android.graphics.Color import android.os.Bundle diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/Common.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/Common.kt index 60e2243..80ea31a 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/Common.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/Common.kt @@ -9,14 +9,13 @@ import android.os.Environment import android.util.Log import android.view.View import android.view.ViewGroup +import android.webkit.MimeTypeMap 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.photo.ResultPhotosFiles -import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx +import com.ux.video.file.filerecovery.db.ResultDataFiles import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import java.io.File import java.util.Date import java.util.Locale import kotlin.collections.sortedBy @@ -40,24 +39,25 @@ object Common { val rootDir = Environment.getExternalStorageDirectory() val dateFormat = SimpleDateFormat("MMMM d,yyyy", Locale.ENGLISH) val chineseFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINESE) + val itemDateFormat = SimpleDateFormat("MM-dd", Locale.CHINESE) val recoveryPhotoDir = "MyAllRecovery/Photo" /** * 默认按照日期分类,将最新的排前面 降序 */ - fun getSortByDayNewToOldInit(list: List): List>> { + fun getSortByDayNewToOldInit(list: List): List>> { val grouped = list.groupBy { dateFormat.format(Date(it.lastModified)) } - val parentData: List>> = grouped + val parentData: List>> = grouped .map { it.key to it.value } .sortedByDescending { dateFormat.parse(it.first)?.time ?: 0L } return parentData } fun getSortByDayNewToOld( - list: List>> - ): List>> { + list: List>> + ): List>> { return list.sortedByDescending { pair -> dateFormat.parse(pair.first)?.time ?: 0L } @@ -68,28 +68,28 @@ object Common { * 按照日期排序, 时间最早的排前面 升序 * */ - fun getSortByDayOldToNew(list: List>>) = + fun getSortByDayOldToNew(list: List>>) = list.sortedBy { dateFormat.parse(it.first)?.time ?: 0L } /** * 按照文件大小排序,将最大的排前面 降序 */ - fun getSortBySizeBigToSmall(list: List) = list.sortedByDescending { + fun getSortBySizeBigToSmall(list: List) = list.sortedByDescending { it.size } /** * 按照文件大小排序,将最小的排前面 升序 */ - fun getSortBySizeSmallToBig(list: List) = list.sortedBy { + fun getSortBySizeSmallToBig(list: List) = list.sortedBy { it.size } fun searchByName( - data: List>>, + data: List>>, keyword: String - ): List>> { + ): List>> { if (keyword.isBlank()) return data return data.mapNotNull { (key, files) -> @@ -98,9 +98,9 @@ object Common { } } fun searchByNameList( - list: List, + list: List, keyword: String - ): List { + ): List { if (keyword.isBlank()) return list return list.filter { it.name.contains(keyword, ignoreCase = true) } } @@ -135,8 +135,8 @@ object Common { * @param months 筛选months月之内的数据 */ fun filterWithinOneMonthByDay( - grouped: List>>, months: Int - ): List>> { + grouped: List>>, months: Int + ): List>> { val today = Calendar.getInstance() val oneMonthAgo = Calendar.getInstance().apply { add(Calendar.MONTH, -months) // 1 个月前 @@ -151,7 +151,7 @@ object Common { /** * @param months 筛选months月之内的数据 */ - fun filterWithinOneMonth(list: List, months: Int): List { + fun filterWithinOneMonth(list: List, months: Int): List { val today = Calendar.getInstance() val oneMonthAgo = Calendar.getInstance().apply { add(Calendar.MONTH, -months) @@ -184,10 +184,10 @@ object Common { * @return 显示的选中数量和选中集合 */ fun checkSelectListContainDate( - list: List>>, - selected: Set - ): Pair> { - val currentSelected = mutableSetOf() + list: List>>, + selected: Set + ): Pair> { + val currentSelected = mutableSetOf() val totalSelectedCount = list.sumOf { pair -> pair.second.count { @@ -202,10 +202,10 @@ object Common { } suspend fun checkSelectListContainDateAsync( - list: List>>, - selected: Set - ): Pair> = withContext(Dispatchers.Default) { - val currentSelected = mutableSetOf() + list: List>>, + selected: Set + ): Pair> = withContext(Dispatchers.Default) { + val currentSelected = mutableSetOf() var totalSelectedCount = 0 // 高效遍历外层 + 内层列表 @@ -238,10 +238,10 @@ object Common { suspend fun checkSelectListContainSize( - list: List, - selected: Set - ): Pair> = withContext(Dispatchers.Default) { - val currentSelected = mutableSetOf() + list: List, + selected: Set + ): Pair> = withContext(Dispatchers.Default) { + val currentSelected = mutableSetOf() var totalSelectedCount = 0 // 高效遍历外层 + 内层列表 @@ -259,8 +259,8 @@ object Common { * 去掉缩略图的集合 */ suspend fun filterThumbnailsAsync( - originalList: MutableList>> - ): List>> = withContext(Dispatchers.Default) { + originalList: MutableList>> + ): List>> = withContext(Dispatchers.Default) { originalList.asSequence() .map { (key, files) -> key to files.asSequence().filter { !it.isThumbnail }.toList() @@ -271,9 +271,9 @@ object Common { fun removeSelectedFromList( - list: List>>, - selectedLiveData: Set - ): List>> { + list: List>>, + selectedLiveData: Set + ): List>> { return list.mapNotNull { (key, files) -> val filtered = files.filterNot { it in selectedLiveData } if (filtered.isNotEmpty()) key to filtered else null @@ -281,9 +281,9 @@ object Common { } fun removeSelectedFromSizeList( - list: List, - selectedLiveData: Set - ): List { + list: List, + selectedLiveData: Set + ): List { return list.filterNot { it in selectedLiveData } } @@ -304,6 +304,7 @@ object Common { } + @SuppressLint("DefaultLocale") fun formatDuration(ms: Long): String { val totalSeconds = ms / 1000 val hours = totalSeconds / 3600 @@ -316,11 +317,66 @@ object Common { 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 { return dateFormat.format(Date(time)) } + fun getItemMonthDay(time: Long): String{ + return itemDateFormat.format(time) + } fun getChineseFormatDate(date: Date): String { return chineseFormat.format(date) diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt index 5a60590..1a16505 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt @@ -7,7 +7,7 @@ import android.os.Build import android.os.Parcelable import android.util.TypedValue 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.withContext import java.util.Date @@ -40,11 +40,11 @@ object ExtendFunctions { } - fun List.filterWithinDateRangeList( + fun List.filterWithinDateRangeList( months: Int = -1, startDate: Date? = null, endDate: Date? = null - ): List { + ): List { val today = Calendar.getInstance() @@ -79,11 +79,11 @@ object ExtendFunctions { - fun List>>.filterWithinDateRange( + fun List>>.filterWithinDateRange( months: Int = -1, startDate: Date? = null, endDate: Date? = null - ): List>> { + ): List>> { val sdf = Common.dateFormat val today = Calendar.getInstance() @@ -118,10 +118,10 @@ object ExtendFunctions { /** * 按文件大小筛选:区间 [minSize, maxSize] */ - fun List.filterBySizeList( + fun List.filterBySizeList( minSize: Long, maxSize: Long - ): List { + ): List { if (minSize == -1L) return this return this.filter { it.size in minSize..maxSize } } @@ -129,10 +129,10 @@ object ExtendFunctions { /** * 按文件大小筛选:区间 [minSize, maxSize] */ - fun List.filterByDurationList( + fun List.filterByDurationList( minSize: Long, maxSize: Long - ): List { + ): List { if (minSize == -1L) return this return this.filter { it.duration in minSize..maxSize } } @@ -140,10 +140,10 @@ object ExtendFunctions { /** * 分组数据:按大小筛选 ,图片和文件筛选文件大小 */ - fun List>>.filterBySize( + fun List>>.filterBySize( minSize: Long, maxSize: Long - ): List>> { + ): List>> { if (minSize == -1L) return this return this.mapNotNull { (date, files) -> val filtered = files.filter { it.size in minSize..maxSize } @@ -154,10 +154,10 @@ object ExtendFunctions { /** * 分组数据:按大小筛选 ,音视频筛选时长 */ - fun List>>.filterByDuration( + fun List>>.filterByDuration( minSize: Long, maxSize: Long - ): List>> { + ): List>> { if (minSize == -1L) return this return this.mapNotNull { (date, files) -> val filtered = files.filter { it.duration in minSize..maxSize } @@ -181,7 +181,7 @@ object ExtendFunctions { /** * 移除掉缩略图后的数据 */ - suspend fun List>>.filterThumbnailsAsync(): List>> = + suspend fun List>>.filterThumbnailsAsync(): List>> = withContext(Dispatchers.Default) { this@filterThumbnailsAsync.asSequence() .mapNotNull { (key, files) -> @@ -195,7 +195,7 @@ object ExtendFunctions { /** * 移除掉缩略图后的数据 */ - suspend fun List.filterRemoveThumbnailsAsync(): List = + suspend fun List.filterRemoveThumbnailsAsync(): List = withContext(Dispatchers.Default) { this@filterRemoveThumbnailsAsync.asSequence() .filter { !it.isThumbnail } // 去掉 isThumbnail = true 的项 @@ -203,9 +203,9 @@ object ExtendFunctions { } - fun List>>.removeItem( - target: ResultPhotosFiles - ): List>> { + fun List>>.removeItem( + target: ResultDataFiles + ): List>> { return this.mapNotNull { (key, files) -> val updatedFiles = files.filterNot { it == target } if (updatedFiles.isNotEmpty()) key to updatedFiles else null diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt index 8e7dc8b..bd4c1f9 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt @@ -5,14 +5,11 @@ import android.content.Context import android.graphics.BitmapFactory import android.media.MediaMetadataRetriever import android.net.Uri -import android.os.Build -import android.os.Environment import android.provider.OpenableColumns +import android.text.format.Formatter import android.util.Log -import androidx.annotation.RequiresApi -import com.ux.video.file.filerecovery.photo.ResultPhotos -import com.ux.video.file.filerecovery.photo.ResultPhotosFiles -import com.ux.video.file.filerecovery.result.ScanningActivity +import com.ux.video.file.filerecovery.db.ResultData +import com.ux.video.file.filerecovery.db.ResultDataFiles 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_documents @@ -27,7 +24,6 @@ import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File @@ -108,20 +104,21 @@ object ScanManager { scanDocuments(root, depth = 0) val map = result.map { (dir, files) -> - val resultPhotosFilesList = files.map { file -> - ResultPhotosFiles( + val resultDataFilesList = files.map { file -> + ResultDataFiles( name = file.name, path = file.absolutePath, size = file.length(), - sizeString = android.text.format.Formatter.formatFileSize( + sizeString = Formatter.formatFileSize( context, file.length() ), 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))) } @@ -168,7 +165,7 @@ object ScanManager { val result = mutableMapOf>() var fileCount = 0 - @RequiresApi(Build.VERSION_CODES.R) + suspend fun scanDir(dir: File, depth: Int, insideHidden: Boolean = false) { if (!dir.exists() || !dir.isDirectory) return if (depth > maxDepth || fileCount >= maxFiles) return @@ -211,28 +208,40 @@ object ScanManager { } } scanDir(root, depth = 0) - ScanManager.showLog("HiddenScan", " 3333") val map = result.map { (dir, files) -> - val resultPhotosFilesList = files.map { file -> - ResultPhotosFiles( + val resultDataFilesList = files.map { file -> + ResultDataFiles( name = file.name, path = file.absolutePath, size = file.length(), - sizeString = android.text.format.Formatter.formatFileSize( + sizeString = Formatter.formatFileSize( context, file.length() ), 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))) } + + 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 { val uri = Uri.fromFile(file) context.contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null) @@ -291,13 +300,14 @@ object ScanManager { * @param folder "AllRecovery/Photo" */ fun CoroutineScope.copySelectedFilesAsync( - selectedSet: Set, + selectedSet: Set, rootDir: File = Common.rootDir, folder: String, - onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit, + onProgress: (currentCounts: Int, data:ResultDataFiles,fileName: String, success: Boolean) -> Unit, onComplete: (currentCounts: Int) -> Unit ) { launch(Dispatchers.IO) { + var recoveryCount = 0 val targetDir = File(rootDir, folder) if (!targetDir.exists()) targetDir.mkdirs() selectedSet.forEachIndexed { index, resultPhotosFiles -> @@ -312,17 +322,19 @@ object ScanManager { } } success = true + recoveryCount++ + withContext(Dispatchers.Main) { + onProgress(index + 1,resultPhotosFiles, srcFile.name, success) + } } catch (e: Exception) { e.printStackTrace() } - withContext(Dispatchers.Main) { - onProgress(index + 1, srcFile.name, success) - } + } } withContext(Dispatchers.Main) { - onComplete(selectedSet.size) + onComplete(recoveryCount) } } } @@ -333,8 +345,8 @@ object ScanManager { * */ fun CoroutineScope.deleteFilesAsync( - selectedSet: Set, - onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit, + selectedSet: Set, + onProgress: (currentCounts: Int, data:ResultDataFiles,fileName: String, success: Boolean) -> Unit, onComplete: (currentCounts: Int) -> Unit ) { launch(Dispatchers.IO) { @@ -346,16 +358,20 @@ object ScanManager { deletedCount++ } withContext(Dispatchers.Main) { - onProgress(index + 1, file.name, true) + onProgress(index + 1, resultPhotosFiles,file.name, true) } } catch (e: Exception) { - onProgress(index + 1, resultPhotosFiles.path!!, false) + onProgress(index + 1,resultPhotosFiles, resultPhotosFiles.path!!, false) } } withContext(Dispatchers.Main) { - onComplete(selectedSet.size) + onComplete(deletedCount) } } } + + + + } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt index 6401932..d333fd2 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt @@ -1,13 +1,9 @@ package com.ux.video.file.filerecovery.utils -import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.ux.video.file.filerecovery.photo.ResultPhotos -import com.ux.video.file.filerecovery.photo.ResultPhotosFiles -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +import com.ux.video.file.filerecovery.db.ResultDataFiles class ScanRepository : ViewModel() { @@ -20,17 +16,17 @@ class ScanRepository : ViewModel() { - private val _selectedLiveData = MutableLiveData>(emptySet()) - val selectedLiveData: LiveData> = _selectedLiveData + private val _selectedLiveData = MutableLiveData>(emptySet()) + val selectedLiveData: LiveData> = _selectedLiveData // 当前筛选显示的选中项 - private val _selectedDisplayLiveData = MutableLiveData>(emptySet()) - val selectedDisplayLiveData: LiveData> = _selectedDisplayLiveData + private val _selectedDisplayLiveData = MutableLiveData>(emptySet()) + val selectedDisplayLiveData: LiveData> = _selectedDisplayLiveData - fun toggleSelection(isAdd: Boolean, resultPhotosFiles: ResultPhotosFiles) { + fun toggleSelection(isAdd: Boolean, resultDataFiles: ResultDataFiles) { val current = _selectedLiveData.value?.toMutableSet() ?: mutableSetOf() val currentDisplay = _selectedDisplayLiveData.value?.toMutableSet() ?: mutableSetOf() - resultPhotosFiles.let { + resultDataFiles.let { if (isAdd) { current.add(it) currentDisplay.add(it) @@ -52,7 +48,7 @@ class ScanRepository : ViewModel() { /** * 数据筛选后或者缩略图显示开关切换后 重置当前显示的选中集合 */ - fun filterResetDisplayFlow(list: MutableSet){ + fun filterResetDisplayFlow(list: MutableSet){ _selectedDisplayLiveData.value = list.toSet() 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 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 - return current?.contains(resultPhotosFiles) == true + return current?.contains(resultDataFiles) == true } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanState.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanState.kt index 2d792b7..c8dc346 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanState.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanState.kt @@ -1,9 +1,9 @@ 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 { data class Progress(val scannedCount: Int,val filePath: String) : ScanState() - data class Complete(val result: ArrayList) : ScanState() + data class Complete(val result: ArrayList) : ScanState() } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/video/VideoPlayActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/video/VideoPlayActivity.kt index 1cf9ebc..c79ec6a 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/video/VideoPlayActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/video/VideoPlayActivity.kt @@ -3,27 +3,18 @@ package com.ux.video.file.filerecovery.video import android.content.Intent import android.net.Uri import android.os.Build -import android.os.Bundle import android.os.Handler import android.os.Looper 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.media3.common.MediaItem import androidx.media3.common.Player import androidx.media3.exoplayer.ExoPlayer import com.ux.video.file.filerecovery.R 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.photo.PhotoInfoActivity -import com.ux.video.file.filerecovery.photo.PhotoInfoActivity.Companion.KEY_CLICK_ITEM -import com.ux.video.file.filerecovery.photo.RecoverOrDeleteManager -import com.ux.video.file.filerecovery.photo.ResultPhotosFiles +import com.ux.video.file.filerecovery.sort.RecoverOrDeleteManager +import com.ux.video.file.filerecovery.db.ResultDataFiles import com.ux.video.file.filerecovery.success.RecoverySuccessActivity import com.ux.video.file.filerecovery.utils.Common @@ -34,7 +25,7 @@ class VideoPlayActivity : BaseActivity() { } private lateinit var player: ExoPlayer - private var myData: ResultPhotosFiles? = null + private var myData: ResultDataFiles? = null private val updateHandler = Handler(Looper.getMainLooper()) override fun inflateBinding(inflater: LayoutInflater): ActivityVideoPlayBinding = ActivityVideoPlayBinding.inflate(inflater) @@ -48,7 +39,7 @@ class VideoPlayActivity : BaseActivity() { override fun initData() { super.initData() 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 { @Suppress("DEPRECATION") intent.getParcelableExtra(KEY_DATA) diff --git a/app/src/main/res/color/selector_recovery_file_tab_layout_title.xml b/app/src/main/res/color/selector_recovery_file_tab_layout_title.xml new file mode 100644 index 0000000..c3b76b2 --- /dev/null +++ b/app/src/main/res/color/selector_recovery_file_tab_layout_title.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_apk.png b/app/src/main/res/drawable/icon_apk.png new file mode 100644 index 0000000..8123e3f Binary files /dev/null and b/app/src/main/res/drawable/icon_apk.png differ diff --git a/app/src/main/res/drawable/icon_doc.png b/app/src/main/res/drawable/icon_doc.png new file mode 100644 index 0000000..5382599 Binary files /dev/null and b/app/src/main/res/drawable/icon_doc.png differ diff --git a/app/src/main/res/drawable/icon_pdf.png b/app/src/main/res/drawable/icon_pdf.png new file mode 100644 index 0000000..1790653 Binary files /dev/null and b/app/src/main/res/drawable/icon_pdf.png differ diff --git a/app/src/main/res/drawable/icon_ppt.png b/app/src/main/res/drawable/icon_ppt.png new file mode 100644 index 0000000..a962032 Binary files /dev/null and b/app/src/main/res/drawable/icon_ppt.png differ diff --git a/app/src/main/res/drawable/icon_txt.png b/app/src/main/res/drawable/icon_txt.png new file mode 100644 index 0000000..b43003e Binary files /dev/null and b/app/src/main/res/drawable/icon_txt.png differ diff --git a/app/src/main/res/drawable/icon_unknow.png b/app/src/main/res/drawable/icon_unknow.png new file mode 100644 index 0000000..94aa84e Binary files /dev/null and b/app/src/main/res/drawable/icon_unknow.png differ diff --git a/app/src/main/res/drawable/icon_xls.png b/app/src/main/res/drawable/icon_xls.png new file mode 100644 index 0000000..d53e56e Binary files /dev/null and b/app/src/main/res/drawable/icon_xls.png differ diff --git a/app/src/main/res/layout/activity_photo_info.xml b/app/src/main/res/layout/activity_photo_info.xml index 85094a3..6ced089 100644 --- a/app/src/main/res/layout/activity_photo_info.xml +++ b/app/src/main/res/layout/activity_photo_info.xml @@ -7,7 +7,7 @@ android:layout_height="match_parent" android:background="@color/white" android:orientation="vertical" - tools:context=".photo.PhotoInfoActivity"> + tools:context=".sort.PhotoInfoActivity"> + tools:context=".sort.PhotoSortingActivity"> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_recovery_photo.xml b/app/src/main/res/layout/fragment_recovery_photo.xml new file mode 100644 index 0000000..f56348d --- /dev/null +++ b/app/src/main/res/layout/fragment_recovery_photo.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/one_audio_documents_item.xml b/app/src/main/res/layout/one_audio_item.xml similarity index 100% rename from app/src/main/res/layout/one_audio_documents_item.xml rename to app/src/main/res/layout/one_audio_item.xml diff --git a/app/src/main/res/layout/one_documents_item.xml b/app/src/main/res/layout/one_documents_item.xml new file mode 100644 index 0000000..8ff3843 --- /dev/null +++ b/app/src/main/res/layout/one_documents_item.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/tab_layout_item.xml b/app/src/main/res/layout/tab_layout_item.xml new file mode 100644 index 0000000..97a87ce --- /dev/null +++ b/app/src/main/res/layout/tab_layout_item.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dfe6ec8..bfbdea8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -86,6 +86,8 @@ wait.. View Sorry!No %s found! Search + Recovered files + (%d) diff --git a/build.gradle.kts b/build.gradle.kts index 922f551..d3798e1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,4 +2,10 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false +} +buildscript { + + dependencies { + classpath("io.objectbox:objectbox-gradle-plugin:4.0.3") + } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0227ce2..0d3cf20 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,9 @@ appcompat = "1.7.1" material = "1.12.0" activity = "1.10.1" constraintlayout = "2.2.1" +lifecycleLivedataKtx = "2.9.4" +lifecycleViewmodelKtx = "2.9.4" +fragmentKtx = "1.8.9" [libraries] 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" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } 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] android-application = { id = "com.android.application", version.ref = "agp" }