增加数据库
@ -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)
|
||||
|
||||
78
app/objectbox-models/default.json
Normal 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
|
||||
}
|
||||
@ -2,12 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Android 10 及以下 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" /> <!-- Android 11+ -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Android 11+ -->
|
||||
<uses-permission
|
||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
@ -24,14 +20,17 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.FileRecovery"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".recovery.RecoveryActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".video.VideoPlayActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".photo.PhotoInfoActivity"
|
||||
android:name=".sort.PhotoInfoActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".photo.PhotoSortingActivity"
|
||||
android:name=".sort.PhotoSortingActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".result.ScanResultDisplayActivity"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.ux.video.file.filerecovery
|
||||
|
||||
import android.app.Application
|
||||
import com.ux.video.file.filerecovery.db.ObjectBoxManager
|
||||
import org.jaaksi.pickerview.widget.BasePickerView
|
||||
|
||||
class App: Application() {
|
||||
@ -14,5 +15,6 @@ class App: Application() {
|
||||
|
||||
BasePickerView.sDefaultItemSize = 40
|
||||
// BasePickerView.sDefaultDrawIndicator = false
|
||||
ObjectBoxManager.initObjectBoxDb(this)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
// }
|
||||
}
|
||||
@ -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<ResultPhotosFiles>
|
||||
val allFiles: ArrayList<ResultDataFiles>
|
||||
): Parcelable
|
||||
@ -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()) }
|
||||
|
||||
}
|
||||
@ -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<ResultPhotos, DocumentsScanResultAdapterBinding>(mContext) {
|
||||
BaseAdapter<ResultData, DocumentsScanResultAdapterBinding>(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<DocumentsScanResultAdapterBinding>,
|
||||
item: ResultPhotos
|
||||
item: ResultData
|
||||
) {
|
||||
|
||||
holder.vb.run {
|
||||
|
||||
@ -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<ActivityMainBinding>() {
|
||||
@ -95,6 +93,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
currentGoType = ScanSelectTypeActivity.Companion.VALUE_DOCUMENT
|
||||
intentCheck()
|
||||
}
|
||||
layoutRecovery.setOnClickListener {
|
||||
startActivity(Intent(this@MainActivity,RecoveryActivity::class.java))
|
||||
}
|
||||
}
|
||||
binding.tvTitle.setOnClickListener {
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
@ -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)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.ux.video.file.filerecovery.recovery.ui.recoveryphoto
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class RecoveryPhotoViewModel : ViewModel() {
|
||||
|
||||
}
|
||||
@ -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<ActivityScanResultDisplayBinding>
|
||||
private var scanType: Int = VALUE_SCAN_TYPE_photo
|
||||
private var exitDialog: ExitDialogFragment? = null
|
||||
|
||||
private var list: ArrayList<ResultPhotos>? = null
|
||||
private var list: ArrayList<ResultData>? = null
|
||||
|
||||
companion object {
|
||||
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(
|
||||
Intent(
|
||||
this@ScanResultDisplayActivity,
|
||||
|
||||
@ -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<ResultPhotosFiles>) -> Unit
|
||||
var onClickItem: (allFiles: ArrayList<ResultDataFiles>) -> Unit
|
||||
) :
|
||||
BaseAdapter<ResultPhotos, ScanResultDocumentsAdapterBinding>(mContext) {
|
||||
BaseAdapter<ResultData, ScanResultDocumentsAdapterBinding>(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<ScanResultDocumentsAdapterBinding>,
|
||||
item: ResultPhotos
|
||||
item: ResultData
|
||||
) {
|
||||
|
||||
holder.vb.run {
|
||||
|
||||
@ -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<ResultPhotosFiles>) -> Unit
|
||||
var onClickItem: (allFiles: ArrayList<ResultDataFiles>) -> Unit
|
||||
) :
|
||||
BaseAdapter<ResultPhotos, ScanResultAdapterBinding>(mContext) {
|
||||
BaseAdapter<ResultData, ScanResultAdapterBinding>(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<ScanResultAdapterBinding>,
|
||||
item: ResultPhotos
|
||||
item: ResultData
|
||||
) {
|
||||
|
||||
holder.vb.run {
|
||||
|
||||
@ -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
|
||||
@ -1,4 +1,4 @@
|
||||
package com.ux.video.file.filerecovery.photo
|
||||
package com.ux.video.file.filerecovery.sort
|
||||
|
||||
|
||||
import android.content.Context
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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<Pair<String, List<ResultPhotosFiles>>, PhotoDisplayDateAdapterBinding>(mContext) {
|
||||
BaseAdapter<Pair<String, List<ResultDataFiles>>, PhotoDisplayDateAdapterBinding>(mContext) {
|
||||
|
||||
|
||||
private var allSelected: Boolean? = null
|
||||
@ -75,7 +75,7 @@ class PhotoDisplayDateAdapter(
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindItem(
|
||||
holder: VHolder<PhotoDisplayDateAdapterBinding>,
|
||||
item: Pair<String, List<ResultPhotosFiles>>
|
||||
item: Pair<String, List<ResultDataFiles>>
|
||||
) {
|
||||
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<Pair<String, List<ResultPhotosFiles>>>() {
|
||||
object ItemDiffCallback : DiffUtil.ItemCallback<Pair<String, List<ResultDataFiles>>>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: Pair<String, List<ResultPhotosFiles>>,
|
||||
newItem: Pair<String, List<ResultPhotosFiles>>
|
||||
oldItem: Pair<String, List<ResultDataFiles>>,
|
||||
newItem: Pair<String, List<ResultDataFiles>>
|
||||
): Boolean {
|
||||
return oldItem.first == newItem.first
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: Pair<String, List<ResultPhotosFiles>>,
|
||||
newItem: Pair<String, List<ResultPhotosFiles>>
|
||||
oldItem: Pair<String, List<ResultDataFiles>>,
|
||||
newItem: Pair<String, List<ResultDataFiles>>
|
||||
): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
@ -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<ResultPhotosFiles>(mContext) {
|
||||
NewBaseAdapter<ResultDataFiles>(mContext) {
|
||||
|
||||
|
||||
//日期组某一天的数据选择状态维护
|
||||
val dateSelectedMap = mutableSetOf<ResultPhotosFiles>()
|
||||
val dateSelectedMap = mutableSetOf<ResultDataFiles>()
|
||||
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
@ -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<ActivityPhotoInfoBinding>() {
|
||||
}
|
||||
|
||||
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<ActivityPhotoInfoBinding>() {
|
||||
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<ActivityPhotoInfoBinding>() {
|
||||
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<ActivityPhotoInfoBinding>() {
|
||||
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)
|
||||
@ -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<ActivityPhotoSortingBinding>() {
|
||||
private var filterLayoutPopupWindows: FilterPopupWindows? = null
|
||||
|
||||
|
||||
private lateinit var sortBySizeBigToSmall: List<ResultPhotosFiles>
|
||||
private lateinit var sortBySizeSmallToBig: List<ResultPhotosFiles>
|
||||
private lateinit var sortByDateReverse: List<Pair<String, List<ResultPhotosFiles>>>
|
||||
private lateinit var sortedByDatePositive: List<Pair<String, List<ResultPhotosFiles>>>
|
||||
private lateinit var sortBySizeBigToSmall: List<ResultDataFiles>
|
||||
private lateinit var sortBySizeSmallToBig: List<ResultDataFiles>
|
||||
private lateinit var sortByDateReverse: List<Pair<String, List<ResultDataFiles>>>
|
||||
private lateinit var sortedByDatePositive: List<Pair<String, List<ResultDataFiles>>>
|
||||
|
||||
//最新显示的数据集合(包含缩略图 ,只保存当前筛选后或者排序后显示的数据,不受switch切换影响)
|
||||
private var currentDateList: List<Pair<String, List<ResultPhotosFiles>>>? = null
|
||||
private var currentSizeList: List<ResultPhotosFiles>? = null
|
||||
private var currentDateList: List<Pair<String, List<ResultDataFiles>>>? = 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
|
||||
|
||||
@ -125,7 +126,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
||||
override fun initData() {
|
||||
super.initData()
|
||||
scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo)
|
||||
val list: ArrayList<ResultPhotosFiles>? =
|
||||
val list: ArrayList<ResultDataFiles>? =
|
||||
intent.getParcelableArrayListExtraCompat(KEY_PHOTO_FOLDER_FILE)
|
||||
mItemDecoration =
|
||||
GridSpacingItemDecoration(columns, Common.itemSpacing, Common.horizontalSpacing)
|
||||
@ -338,7 +339,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
||||
is PhotoDisplayDateAdapter -> {
|
||||
dateAdapter?.setAllSelected(it.isSelected)
|
||||
dateAdapter?.getCurrentData()?.let {
|
||||
it as List<Pair<String, List<ResultPhotosFiles>>>
|
||||
it as List<Pair<String, List<ResultDataFiles>>>
|
||||
if (it.size > 0)
|
||||
Common.showLog("------------全选按钮 日期-${it.size} ${it[0].second[0].path}")
|
||||
}
|
||||
@ -348,7 +349,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
||||
is PhotoDisplayDateChildAdapter -> {
|
||||
sizeSortAdapter?.setAllSelected(it.isSelected)
|
||||
sizeSortAdapter?.getCurrentData()?.let {
|
||||
it as List<ResultPhotosFiles>
|
||||
it as List<ResultDataFiles>
|
||||
if (it.size > 0)
|
||||
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 }
|
||||
return currentSizeList!!
|
||||
}
|
||||
|
||||
private fun resetCurrentSizeList(currentList: List<ResultPhotosFiles>) {
|
||||
private fun resetCurrentSizeList(currentList: List<ResultDataFiles>) {
|
||||
currentSizeList = currentList
|
||||
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!!)
|
||||
return currentDateList!!
|
||||
}
|
||||
|
||||
private fun resetCurrentDateList(currentList: List<Pair<String, List<ResultPhotosFiles>>>) {
|
||||
private fun resetCurrentDateList(currentList: List<Pair<String, List<ResultDataFiles>>>) {
|
||||
currentDateList = currentList
|
||||
currentSizeList = null
|
||||
val totalSelectedCount = currentList.sumOf { pair ->
|
||||
@ -484,7 +485,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
||||
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<ActivityPhotoSortingBinding>() {
|
||||
* 数据筛选或者缩略图切换显示后,对比刷新实际显示的选中数据
|
||||
*/
|
||||
private fun checkRefreshDisPlaySelected(
|
||||
list1: List<Pair<String, List<ResultPhotosFiles>>>? = null,
|
||||
list2: List<ResultPhotosFiles>? = null
|
||||
list1: List<Pair<String, List<ResultDataFiles>>>? = null,
|
||||
list2: List<ResultDataFiles>? = null
|
||||
) {
|
||||
lifecycleScope.launch {
|
||||
list1?.let {
|
||||
@ -887,13 +888,13 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
||||
//选中集合的更新
|
||||
viewModel.afterDeleted()
|
||||
deferredResults["sizeList"]?.let { list ->
|
||||
list as List<ResultPhotosFiles>
|
||||
list as List<ResultDataFiles>
|
||||
Common.showLog("---------更新 sizeList = ${list.size}")
|
||||
sizeSortAdapter?.setData(list)
|
||||
resetCurrentSizeList(list)
|
||||
}
|
||||
deferredResults["dateList"]?.let { list ->
|
||||
list as List<Pair<String, List<ResultPhotosFiles>>>
|
||||
list as List<Pair<String, List<ResultDataFiles>>>
|
||||
Common.showLog("---------更新 dateList = ${list.size}")
|
||||
dateAdapter?.setData(list)
|
||||
resetCurrentDateList(list)
|
||||
@ -932,13 +933,13 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
|
||||
//选中集合的更新
|
||||
viewModel.afterSingleDeleted(deletedData)
|
||||
deferredResults["sizeList"]?.let { list ->
|
||||
list as List<ResultPhotosFiles>
|
||||
list as List<ResultDataFiles>
|
||||
Common.showLog("---------更新 sizeList = ${list.size}")
|
||||
sizeSortAdapter?.setData(list)
|
||||
resetCurrentSizeList(list)
|
||||
}
|
||||
deferredResults["dateList"]?.let { list ->
|
||||
list as List<Pair<String, List<ResultPhotosFiles>>>
|
||||
list as List<Pair<String, List<ResultDataFiles>>>
|
||||
Common.showLog("---------更新 dateList = ${list.size}")
|
||||
dateAdapter?.setData(list)
|
||||
resetCurrentDateList(list)
|
||||
@ -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<ResultPhotosFiles>,
|
||||
selectedSetList: Set<ResultDataFiles>,
|
||||
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<ResultPhotosFiles>,
|
||||
selectedSetList: Set<ResultDataFiles>,
|
||||
onComplete: (number: Int) -> Unit
|
||||
) {
|
||||
dialogConfirmDelete = dialogConfirmDelete ?: ConfirmDeleteDialogFragment()
|
||||
@ -86,18 +90,10 @@ object RecoverOrDeleteManager {
|
||||
isInfoDelete: Boolean = false,
|
||||
fragmentManager: FragmentManager,
|
||||
scope: CoroutineScope,
|
||||
selectedSetList: Set<ResultPhotosFiles>,
|
||||
selectedSetList: Set<ResultDataFiles>,
|
||||
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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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<ResultPhotosFiles>): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
fun getSortByDayNewToOldInit(list: List<ResultDataFiles>): List<Pair<String, List<ResultDataFiles>>> {
|
||||
val grouped = list.groupBy {
|
||||
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 }
|
||||
.sortedByDescending { dateFormat.parse(it.first)?.time ?: 0L }
|
||||
return parentData
|
||||
}
|
||||
|
||||
fun getSortByDayNewToOld(
|
||||
list: List<Pair<String, List<ResultPhotosFiles>>>
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
list: List<Pair<String, List<ResultDataFiles>>>
|
||||
): List<Pair<String, List<ResultDataFiles>>> {
|
||||
return list.sortedByDescending { pair ->
|
||||
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 }
|
||||
|
||||
/**
|
||||
* 按照文件大小排序,将最大的排前面 降序
|
||||
*/
|
||||
fun getSortBySizeBigToSmall(list: List<ResultPhotosFiles>) = list.sortedByDescending {
|
||||
fun getSortBySizeBigToSmall(list: List<ResultDataFiles>) = list.sortedByDescending {
|
||||
it.size
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照文件大小排序,将最小的排前面 升序
|
||||
*/
|
||||
fun getSortBySizeSmallToBig(list: List<ResultPhotosFiles>) = list.sortedBy {
|
||||
fun getSortBySizeSmallToBig(list: List<ResultDataFiles>) = list.sortedBy {
|
||||
it.size
|
||||
}
|
||||
|
||||
|
||||
fun searchByName(
|
||||
data: List<Pair<String, List<ResultPhotosFiles>>>,
|
||||
data: List<Pair<String, List<ResultDataFiles>>>,
|
||||
keyword: String
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
): List<Pair<String, List<ResultDataFiles>>> {
|
||||
if (keyword.isBlank()) return data
|
||||
|
||||
return data.mapNotNull { (key, files) ->
|
||||
@ -98,9 +98,9 @@ object Common {
|
||||
}
|
||||
}
|
||||
fun searchByNameList(
|
||||
list: List<ResultPhotosFiles>,
|
||||
list: List<ResultDataFiles>,
|
||||
keyword: String
|
||||
): List<ResultPhotosFiles> {
|
||||
): List<ResultDataFiles> {
|
||||
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<Pair<String, List<ResultPhotosFiles>>>, months: Int
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
grouped: List<Pair<String, List<ResultDataFiles>>>, months: Int
|
||||
): List<Pair<String, List<ResultDataFiles>>> {
|
||||
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<ResultPhotosFiles>, months: Int): List<ResultPhotosFiles> {
|
||||
fun filterWithinOneMonth(list: List<ResultDataFiles>, months: Int): List<ResultDataFiles> {
|
||||
val today = Calendar.getInstance()
|
||||
val oneMonthAgo = Calendar.getInstance().apply {
|
||||
add(Calendar.MONTH, -months)
|
||||
@ -184,10 +184,10 @@ object Common {
|
||||
* @return 显示的选中数量和选中集合
|
||||
*/
|
||||
fun checkSelectListContainDate(
|
||||
list: List<Pair<String, List<ResultPhotosFiles>>>,
|
||||
selected: Set<ResultPhotosFiles>
|
||||
): Pair<Int, MutableSet<ResultPhotosFiles>> {
|
||||
val currentSelected = mutableSetOf<ResultPhotosFiles>()
|
||||
list: List<Pair<String, List<ResultDataFiles>>>,
|
||||
selected: Set<ResultDataFiles>
|
||||
): Pair<Int, MutableSet<ResultDataFiles>> {
|
||||
val currentSelected = mutableSetOf<ResultDataFiles>()
|
||||
|
||||
val totalSelectedCount = list.sumOf { pair ->
|
||||
pair.second.count {
|
||||
@ -202,10 +202,10 @@ object Common {
|
||||
}
|
||||
|
||||
suspend fun checkSelectListContainDateAsync(
|
||||
list: List<Pair<String, List<ResultPhotosFiles>>>,
|
||||
selected: Set<ResultPhotosFiles>
|
||||
): Pair<Int, MutableSet<ResultPhotosFiles>> = withContext(Dispatchers.Default) {
|
||||
val currentSelected = mutableSetOf<ResultPhotosFiles>()
|
||||
list: List<Pair<String, List<ResultDataFiles>>>,
|
||||
selected: Set<ResultDataFiles>
|
||||
): Pair<Int, MutableSet<ResultDataFiles>> = withContext(Dispatchers.Default) {
|
||||
val currentSelected = mutableSetOf<ResultDataFiles>()
|
||||
var totalSelectedCount = 0
|
||||
|
||||
// 高效遍历外层 + 内层列表
|
||||
@ -238,10 +238,10 @@ object Common {
|
||||
|
||||
|
||||
suspend fun checkSelectListContainSize(
|
||||
list: List<ResultPhotosFiles>,
|
||||
selected: Set<ResultPhotosFiles>
|
||||
): Pair<Int, MutableSet<ResultPhotosFiles>> = withContext(Dispatchers.Default) {
|
||||
val currentSelected = mutableSetOf<ResultPhotosFiles>()
|
||||
list: List<ResultDataFiles>,
|
||||
selected: Set<ResultDataFiles>
|
||||
): Pair<Int, MutableSet<ResultDataFiles>> = withContext(Dispatchers.Default) {
|
||||
val currentSelected = mutableSetOf<ResultDataFiles>()
|
||||
var totalSelectedCount = 0
|
||||
|
||||
// 高效遍历外层 + 内层列表
|
||||
@ -259,8 +259,8 @@ object Common {
|
||||
* 去掉缩略图的集合
|
||||
*/
|
||||
suspend fun filterThumbnailsAsync(
|
||||
originalList: MutableList<Pair<String, List<ResultPhotosFiles>>>
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> = withContext(Dispatchers.Default) {
|
||||
originalList: MutableList<Pair<String, List<ResultDataFiles>>>
|
||||
): List<Pair<String, List<ResultDataFiles>>> = 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<Pair<String, List<ResultPhotosFiles>>>,
|
||||
selectedLiveData: Set<ResultPhotosFiles>
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
list: List<Pair<String, List<ResultDataFiles>>>,
|
||||
selectedLiveData: Set<ResultDataFiles>
|
||||
): List<Pair<String, List<ResultDataFiles>>> {
|
||||
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<ResultPhotosFiles>,
|
||||
selectedLiveData: Set<ResultPhotosFiles>
|
||||
): List<ResultPhotosFiles> {
|
||||
list: List<ResultDataFiles>,
|
||||
selectedLiveData: Set<ResultDataFiles>
|
||||
): List<ResultDataFiles> {
|
||||
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)
|
||||
|
||||
@ -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<ResultPhotosFiles>.filterWithinDateRangeList(
|
||||
fun List<ResultDataFiles>.filterWithinDateRangeList(
|
||||
months: Int = -1,
|
||||
startDate: Date? = null,
|
||||
endDate: Date? = null
|
||||
): List<ResultPhotosFiles> {
|
||||
): List<ResultDataFiles> {
|
||||
|
||||
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,
|
||||
startDate: Date? = null,
|
||||
endDate: Date? = null
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
): List<Pair<String, List<ResultDataFiles>>> {
|
||||
|
||||
val sdf = Common.dateFormat
|
||||
val today = Calendar.getInstance()
|
||||
@ -118,10 +118,10 @@ object ExtendFunctions {
|
||||
/**
|
||||
* 按文件大小筛选:区间 [minSize, maxSize]
|
||||
*/
|
||||
fun List<ResultPhotosFiles>.filterBySizeList(
|
||||
fun List<ResultDataFiles>.filterBySizeList(
|
||||
minSize: Long,
|
||||
maxSize: Long
|
||||
): List<ResultPhotosFiles> {
|
||||
): List<ResultDataFiles> {
|
||||
if (minSize == -1L) return this
|
||||
return this.filter { it.size in minSize..maxSize }
|
||||
}
|
||||
@ -129,10 +129,10 @@ object ExtendFunctions {
|
||||
/**
|
||||
* 按文件大小筛选:区间 [minSize, maxSize]
|
||||
*/
|
||||
fun List<ResultPhotosFiles>.filterByDurationList(
|
||||
fun List<ResultDataFiles>.filterByDurationList(
|
||||
minSize: Long,
|
||||
maxSize: Long
|
||||
): List<ResultPhotosFiles> {
|
||||
): List<ResultDataFiles> {
|
||||
if (minSize == -1L) return this
|
||||
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,
|
||||
maxSize: Long
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
): List<Pair<String, List<ResultDataFiles>>> {
|
||||
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<Pair<String, List<ResultPhotosFiles>>>.filterByDuration(
|
||||
fun List<Pair<String, List<ResultDataFiles>>>.filterByDuration(
|
||||
minSize: Long,
|
||||
maxSize: Long
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
): List<Pair<String, List<ResultDataFiles>>> {
|
||||
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<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) {
|
||||
this@filterThumbnailsAsync.asSequence()
|
||||
.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) {
|
||||
this@filterRemoveThumbnailsAsync.asSequence()
|
||||
.filter { !it.isThumbnail } // 去掉 isThumbnail = true 的项
|
||||
@ -203,9 +203,9 @@ object ExtendFunctions {
|
||||
}
|
||||
|
||||
|
||||
fun List<Pair<String, List<ResultPhotosFiles>>>.removeItem(
|
||||
target: ResultPhotosFiles
|
||||
): List<Pair<String, List<ResultPhotosFiles>>> {
|
||||
fun List<Pair<String, List<ResultDataFiles>>>.removeItem(
|
||||
target: ResultDataFiles
|
||||
): List<Pair<String, List<ResultDataFiles>>> {
|
||||
return this.mapNotNull { (key, files) ->
|
||||
val updatedFiles = files.filterNot { it == target }
|
||||
if (updatedFiles.isNotEmpty()) key to updatedFiles else null
|
||||
|
||||
@ -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<String, MutableList<File>>()
|
||||
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<ResultPhotosFiles>,
|
||||
selectedSet: Set<ResultDataFiles>,
|
||||
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<ResultPhotosFiles>,
|
||||
onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit,
|
||||
selectedSet: Set<ResultDataFiles>,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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<Set<ResultPhotosFiles>>(emptySet())
|
||||
val selectedLiveData: LiveData<Set<ResultPhotosFiles>> = _selectedLiveData
|
||||
private val _selectedLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
|
||||
val selectedLiveData: LiveData<Set<ResultDataFiles>> = _selectedLiveData
|
||||
|
||||
// 当前筛选显示的选中项
|
||||
private val _selectedDisplayLiveData = MutableLiveData<Set<ResultPhotosFiles>>(emptySet())
|
||||
val selectedDisplayLiveData: LiveData<Set<ResultPhotosFiles>> = _selectedDisplayLiveData
|
||||
private val _selectedDisplayLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
|
||||
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 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<ResultPhotosFiles>){
|
||||
fun filterResetDisplayFlow(list: MutableSet<ResultDataFiles>){
|
||||
_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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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<ResultPhotos>) : ScanState()
|
||||
data class Complete(val result: ArrayList<ResultData>) : ScanState()
|
||||
}
|
||||
@ -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<ActivityVideoPlayBinding>() {
|
||||
}
|
||||
|
||||
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<ActivityVideoPlayBinding>() {
|
||||
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)
|
||||
|
||||
@ -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>
|
||||
BIN
app/src/main/res/drawable/icon_apk.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
app/src/main/res/drawable/icon_doc.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
app/src/main/res/drawable/icon_pdf.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable/icon_ppt.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
app/src/main/res/drawable/icon_txt.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable/icon_unknow.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
app/src/main/res/drawable/icon_xls.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
tools:context=".photo.PhotoInfoActivity">
|
||||
tools:context=".sort.PhotoInfoActivity">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
tools:context=".photo.PhotoSortingActivity">
|
||||
tools:context=".sort.PhotoSortingActivity">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/layout_top"
|
||||
|
||||
53
app/src/main/res/layout/activity_recovery.xml
Normal 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>
|
||||
20
app/src/main/res/layout/fragment_recovery_photo.xml
Normal 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>
|
||||
78
app/src/main/res/layout/one_documents_item.xml
Normal 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>
|
||||
24
app/src/main/res/layout/tab_layout_item.xml
Normal 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>
|
||||
@ -86,6 +86,8 @@ wait..</string>
|
||||
<string name="view">View</string>
|
||||
<string name="not_found">Sorry!No %s found!</string>
|
||||
<string name="search">Search</string>
|
||||
<string name="recovered_files">Recovered files</string>
|
||||
<string name="text_counts">(%d)</string>
|
||||
|
||||
|
||||
<string-array name="filter_date">
|
||||
|
||||
@ -3,3 +3,9 @@ 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")
|
||||
}
|
||||
}
|
||||
@ -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" }
|
||||
|
||||