diff --git a/app/FileRecovery b/app/FileRecovery new file mode 100644 index 0000000..100c791 Binary files /dev/null and b/app/FileRecovery differ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3545135..0bab8ca 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,11 +1,16 @@ +import java.text.SimpleDateFormat +import java.util.Date + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) id ("kotlin-kapt") id ("kotlin-parcelize") id("io.objectbox") + id("com.google.gms.google-services") + id("com.google.firebase.crashlytics") } - +val timestamp = SimpleDateFormat("MM_dd_HH_mm").format(Date()) android { namespace = "com.ux.video.file.filerecovery" compileSdk = 36 @@ -16,13 +21,13 @@ android { targetSdk = 36 versionCode = 1 versionName = "1.0" - + project.setProperty("archivesBaseName", "File Recovery Tool" + versionName + "(${versionCode})_$timestamp") testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" @@ -56,9 +61,17 @@ dependencies { androidTestImplementation(libs.androidx.espresso.core) implementation (libs.glide) kapt (libs.compiler) - implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") + implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.10.0") implementation ("com.google.android.material:material:1.13.0") implementation(project(":pickerview")) implementation ("androidx.media3:media3-exoplayer:1.8.0") implementation ("androidx.media3:media3-ui:1.8.0") +// implementation("androidx.core:core-splashscreen:1.0.1") + implementation("androidx.browser:browser:1.8.0") + + + implementation(platform("com.google.firebase:firebase-bom:34.6.0")) + implementation("com.google.firebase:firebase-crashlytics") + implementation("com.google.firebase:firebase-analytics") + implementation("com.google.firebase:firebase-config") } \ No newline at end of file diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..77f3bdf --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "1034840485270", + "project_id": "file-recovery-3d900", + "storage_bucket": "file-recovery-3d900.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:1034840485270:android:6d9c203fa586c78cde81b7", + "android_client_info": { + "package_name": "com.ux.video.file.filerecovery" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyD6LDj1f2IQeA0oS6TaanB0-_B334k6PBI" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/objectbox-models/default.json b/app/objectbox-models/default.json index 6e25a4d..e80a6ea 100644 --- a/app/objectbox-models/default.json +++ b/app/objectbox-models/default.json @@ -43,11 +43,6 @@ "id": "7:8305655849151310709", "name": "lastModified", "type": 6 - }, - { - "id": "8:5134665607056785092", - "name": "resolution", - "type": 9 } ], "relations": [] @@ -71,7 +66,8 @@ 3831579248666795267, 4445203567182319472, 6858261029979256233, - 2835789057594891780 + 2835789057594891780, + 5134665607056785092 ], "retiredRelationUids": [], "version": 1 diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb43..f320dd5 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,63 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile +# ========= Kotlin Parcelize ========= +-keep class kotlinx.parcelize.Parcelize +-keep class kotlinx.android.parcel.Parcelize +-keepclassmembers class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} + +# ResultDataFiles Parcelable +-keep class com.ux.video.file.filerecovery.db.ResultDataFiles implements android.os.Parcelable { + ; + ; +} + +-keepclassmembers class com.ux.video.file.filerecovery.db.ResultDataFiles { + public static final android.os.Parcelable$Creator CREATOR; +} + +-keep class com.ux.video.file.filerecovery.db.ResultDataFiles { *; } +-keep class com.ux.video.file.filerecovery.db.ResultData { *; } + +-keep enum com.ux.video.file.filerecovery.utils.FileType { *; } +-keep class com.ux.video.file.filerecovery.db.FileTypeConverter { *; } + +-keep class io.objectbox.** { *; } +-keep @io.objectbox.annotation.Entity class * { *; } + + + + + +# ResultDataFiles Parcelable +-keep class com.ux.video.file.filerecovery.db.ResultDataFiles implements android.os.Parcelable { + ; + ; +} + +-keepclassmembers class com.ux.video.file.filerecovery.db.ResultDataFiles { + public static final android.os.Parcelable$Creator CREATOR; +} + +# FileType enum +-keep enum com.ux.video.file.filerecovery.utils.FileType { *; } + +# ObjectBox Converter +-keep class com.ux.video.file.filerecovery.db.FileTypeConverter { *; } + +# ObjectBox +-keep class io.objectbox.** { *; } +-keep @io.objectbox.annotation.Entity class * { *; } + +# Kotlin Parcelize support +-keep class kotlinx.parcelize.Parcelize +-keep class kotlinx.android.parcel.Parcelize +-keep class kotlin.Metadata { *; } + + + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aea2e06..b461008 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,49 +13,76 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher" + android:icon="@mipmap/recovery" android:label="@string/app_name" android:requestLegacyExternalStorage="true" - android:roundIcon="@mipmap/ic_launcher_round" + android:roundIcon="@mipmap/recovery" android:supportsRtl="true" android:theme="@style/Theme.FileRecovery" tools:targetApi="31"> - - - - - - - + android:name=".welcome.SplashActivity" + android:exported="true" + android:screenOrientation="portrait" + tools:ignore="SplashScreen"> + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/base/BaseIngDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/base/BaseIngDialogFragment.kt index 6e4a9ae..8f7780f 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/base/BaseIngDialogFragment.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/base/BaseIngDialogFragment.kt @@ -13,6 +13,9 @@ import androidx.fragment.app.DialogFragment import com.ux.video.file.filerecovery.databinding.DialogRecoveringBinding +/** + * 删除/恢复中父类弹窗 + */ abstract class BaseIngDialogFragment() : DialogFragment() { var total: Int = 0 var completeListener: ((number: Int) -> Unit)? = null diff --git a/app/src/main/java/com/ux/video/file/filerecovery/db/FileTypeConverter.kt b/app/src/main/java/com/ux/video/file/filerecovery/db/FileTypeConverter.kt new file mode 100644 index 0000000..3e0dd22 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/db/FileTypeConverter.kt @@ -0,0 +1,15 @@ +package com.ux.video.file.filerecovery.db + +import com.ux.video.file.filerecovery.utils.FileType +import io.objectbox.converter.PropertyConverter + +class FileTypeConverter : PropertyConverter { + + override fun convertToDatabaseValue(entityProperty: FileType?): Int? { + return entityProperty?.value + } + + override fun convertToEntityProperty(databaseValue: Int?): FileType? { + return FileType.entries.find { it.value == databaseValue } + } +} diff --git a/app/src/main/java/com/ux/video/file/filerecovery/db/ObjectBoxManager.kt b/app/src/main/java/com/ux/video/file/filerecovery/db/ObjectBoxManager.kt index 6aadeb4..76ddcc6 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/db/ObjectBoxManager.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/db/ObjectBoxManager.kt @@ -1,13 +1,17 @@ package com.ux.video.file.filerecovery.db import android.content.Context +import androidx.lifecycle.LifecycleCoroutineScope import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.FileType import io.objectbox.Box import io.objectbox.BoxStore import io.objectbox.config.DebugFlags import io.objectbox.reactive.DataSubscription +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlin.jvm.java @@ -92,6 +96,21 @@ object ObjectBoxManager { } } + + + + fun deleteRecoveryFilesAsyncById(lifecycleCoroutineScope: LifecycleCoroutineScope,list: List, onComplete: () -> Unit) { + lifecycleCoroutineScope.launch(Dispatchers.IO) { + val objectBox = getDataBox() + val ids = list.map { it.id } + objectBox.removeByIds(ids) + withContext(Dispatchers.Main) { + onComplete() + } + } + } + + // // fun queryIsLike(id: Long): Boolean { // val likeBox: Box? = getLikeBox() @@ -113,10 +132,10 @@ object ObjectBoxManager { return list } - suspend fun queryRecoveryFileAsync(type: Int): List = + suspend fun queryRecoveryFileAsync(type: FileType): List = withContext(Dispatchers.IO) { val dataBox = getDataBox() - dataBox.query(ResultDataFiles_.fileType.equal(type)) + dataBox.query(ResultDataFiles_.fileType.equal(type.value)) .build() .find() } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFiles.kt b/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFiles.kt index 0aebcde..6b9c074 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFiles.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFiles.kt @@ -4,6 +4,8 @@ import java.io.File import android.os.Parcelable import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.FileType +import io.objectbox.annotation.Convert import io.objectbox.annotation.Entity import io.objectbox.annotation.Id import kotlinx.parcelize.Parcelize @@ -13,35 +15,40 @@ import kotlinx.parcelize.Parcelize data class ResultDataFiles( @Id var id: Long = 0, - // 0-3 photo\video\audio\documents - var fileType: Int, + + @Convert(converter = FileTypeConverter::class, dbType = Int::class) + var fileType: FileType, val name: String, val path: String, val size: Long, // 字节 val sizeString: String, val lastModified: Long, // 时间戳 - var resolution: String // 尺寸 ) : Parcelable { - val targetFile: File? - get() = path?.let { File(it) } +// val targetFile: File +// get() = path.let { File(it) } + + //尺寸 +// val resolution: String +// get() = Common.getResolution(fileType, targetFile) //是否为缩略图文件(宽高任一小于 256) - val isThumbnail: Boolean - get() { - val parts = resolution.lowercase().split("*").mapNotNull { - it.trim().toIntOrNull() - } - if (parts.size == 2) { - val (width, height) = parts - return width < 256 || height < 256 - } - return false - } +// val isThumbnail: Boolean +// get() { +// val parts = resolution.lowercase().split("*").mapNotNull { +// it.trim().toIntOrNull() +// } +// if (parts.size == 2) { +// val (width, height) = parts +// return width < 256 || height < 256 +// } +// return false +// } //音视频时长 - val duration: Long - get() { - return Common.getMediaDuration(path.toString()) } +// val duration: Long +// get() { +// return Common.getMediaDuration(path.toString()) +// } } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFilesExt.kt b/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFilesExt.kt new file mode 100644 index 0000000..88abbd5 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/db/ResultDataFilesExt.kt @@ -0,0 +1,37 @@ +package com.ux.video.file.filerecovery.db +import com.ux.video.file.filerecovery.utils.Common +import java.io.File + +/** + * 目标文件 + */ +fun ResultDataFiles.targetFile(): File = + File(this.path) + +/** + * 分辨率(图片 / 视频) + */ +fun ResultDataFiles.resolution(): String = + Common.getResolution(this.fileType, targetFile()) + +/** + * 是否缩略图(宽或高 < 256) + */ +fun ResultDataFiles.isThumbnail(): Boolean { + val parts = resolution() + .lowercase() + .split("*") + .mapNotNull { it.trim().toIntOrNull() } + + if (parts.size == 2) { + val (width, height) = parts + return width < 256 || height < 256 + } + return false +} + +/** + * 音视频时长 + */ +fun ResultDataFiles.duration(): Long = + Common.getMediaDuration(this.path) diff --git a/app/src/main/java/com/ux/video/file/filerecovery/detail/DetailsActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/detail/DetailsActivity.kt new file mode 100644 index 0000000..2aa1bf1 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/detail/DetailsActivity.kt @@ -0,0 +1,300 @@ +package com.ux.video.file.filerecovery.detail + +import android.content.Intent +import android.graphics.drawable.Drawable +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 com.bumptech.glide.Glide +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.RequestOptions +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.ActivityDetailsBinding +import com.ux.video.file.filerecovery.db.ResultDataFiles +import com.ux.video.file.filerecovery.db.duration +import com.ux.video.file.filerecovery.db.resolution +import com.ux.video.file.filerecovery.db.targetFile +import com.ux.video.file.filerecovery.sort.RecoverOrDeleteManager +import com.ux.video.file.filerecovery.success.RecoverySuccessActivity +import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx +import com.ux.video.file.filerecovery.utils.FileType +import com.ux.video.file.filerecovery.utils.ScanType +import com.ux.video.file.filerecovery.video.PlayMediaManager +import com.ux.video.file.filerecovery.video.VideoPlayActivity +import java.io.File + +class DetailsActivity : BaseActivity() { + + companion object { + val KEY_CLICK_ITEM = "click_item" + val KEY_SHOW_SHARE = "show_share" + } + + private var playMediaManager: PlayMediaManager? = null + + private lateinit var fileType: FileType + private var myData: ResultDataFiles? = null + + private var showShare = false + + override fun inflateBinding(inflater: LayoutInflater): ActivityDetailsBinding = + ActivityDetailsBinding.inflate(inflater) + + override fun initView() { + super.initView() + myData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra(KEY_CLICK_ITEM, ResultDataFiles::class.java) + } else { + @Suppress("DEPRECATION") + intent.getParcelableExtra(KEY_CLICK_ITEM) + } + val intExtra = intent.getIntExtra(Common.KEY_FILE_TYPE, FileType.PHOTO.value) + + myData?.fileType.let { + if (it != null) { + fileType = it + Common.showLog("------fileType=${fileType}") + } + } + showShare = intent.getBooleanExtra(KEY_SHOW_SHARE, false) + setView() + + + } + + override fun initData() { + super.initData() + binding.run { + imageViewBack.setOnClickListener { finish() } + myData?.let { resultPhotosFiles -> + tvName.text = resultPhotosFiles.name + tvPath.text = resultPhotosFiles.path + tvSize.text = resultPhotosFiles.sizeString + tvDate.text = Common.getFormatDate(resultPhotosFiles.lastModified) + tvResolution.text = resultPhotosFiles.resolution() + tvDuration.text = Common.formatDuration(resultPhotosFiles.duration()) + resultPhotosFiles.targetFile().let { + tvType.text = Common.getMimeTypeParts(it) + } + } + } + } + + private fun setView() { + binding.run { + layoutBottom.tvLeft.run { + text = resources.getString(R.string.delete) + setOnClickListener { + myData?.let { myData -> + RecoverOrDeleteManager.showConfirmDeleteDialog( + true, + supportFragmentManager, + lifecycleScope, + setOf(myData) + ) { count -> + complete(count, 1) + } + } + } + } + + layoutBottom.tvRight.run { + myData?.let { myData -> + if (showShare) { + text = getString(R.string.share) + setOnClickListener { + Common.shareSingleFile( + this@DetailsActivity, + myData.targetFile(), + myData.fileType + ) + } + } else { + text = getString(R.string.recover) + setOnClickListener { + RecoverOrDeleteManager.showRecoveringDialog( + supportFragmentManager, + lifecycleScope, + setOf(myData) + ) { count -> + complete(count, 0) + } + } + } + } + } + + myData?.let { + + when (it.fileType) { + FileType.PHOTO -> { + layoutName.isVisible = true + layoutPath.isVisible = true + layoutResolution.isVisible = true + layoutDate.isVisible = true + frameImage.setBackgroundResource(0) + + + layoutSeekbar.isVisible = false + layoutType.isVisible = false + layoutSize.isVisible = false + layoutDuration.isVisible = false + + imPlay.isVisible = false + + myData?.targetFile()?.let { loadImage(image, it) } + } + + FileType.VIDEO -> { + layoutName.isVisible = true + layoutPath.isVisible = true + layoutResolution.isVisible = true + layoutDate.isVisible = true + layoutDuration.isVisible = true + frameImage.setBackgroundResource(0) + + + layoutSeekbar.isVisible = false + layoutType.isVisible = false + layoutSize.isVisible = false + + imPlay.isVisible = true + myData?.let { data -> + loadImage(image, data.targetFile()) + frameImage.setOnClickListener { + startActivity( + Intent( + this@DetailsActivity, + VideoPlayActivity::class.java + ).apply { + putExtra(VideoPlayActivity.KEY_DATA, data) + }) + } + } + } + + FileType.AUDIO -> { + Common.showLog("----------音频") + layoutName.isVisible = true + layoutPath.isVisible = true + layoutSize.isVisible = true + layoutDate.isVisible = true + layoutDuration.isVisible = true + layoutSeekbar.isVisible = true + imPlay.isVisible = true + frameImage.setBackgroundResource(R.drawable.bg_info_music_f2f2f7_8) + + loadCenterImage(image, R.drawable.image_info_music) + + initPlayAudio() + layoutResolution.isVisible = false + layoutType.isVisible = false + } + + FileType.DOCUMENT -> { + layoutName.isVisible = true + layoutType.isVisible = true + layoutPath.isVisible = true + layoutSize.isVisible = true + layoutDate.isVisible = true + + 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@DetailsActivity), + 180.dpToPx(this@DetailsActivity) + ) + params.width = 180.dpToPx(this@DetailsActivity) + params.height = 180.dpToPx(this@DetailsActivity) + image.layoutParams = params + } + + private fun loadImage(image: ImageView, file: File) { + Glide.with(this@DetailsActivity) + .load(file) + .error(R.drawable.photo_place_holder) + .apply( + RequestOptions().transform( + CenterCrop(), + RoundedCorners(8.dpToPx(this@DetailsActivity)) + ) + ) + .listener(object : RequestListener { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target, + isFirstResource: Boolean + ): Boolean { + return false + } + + override fun onResourceReady( + resource: Drawable, + model: Any, + target: Target?, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { + + return false + } + + }) + .into(image) + } + + + private fun initPlayAudio() { + myData?.targetFile()?.let { + binding.run { + playMediaManager = PlayMediaManager( + context = this@DetailsActivity, mediaFile = it, + seekBar = seekBar, playBtn = imPlay, onUpdateProgress = { current, total -> + textTimeCurrent.text = current + textTimeTotal.text = total + }) + + } + } + } + + + private fun complete(number: Int, type: Int) { + finish() + startActivity(Intent(this@DetailsActivity, RecoverySuccessActivity::class.java).apply { + putExtra(RecoverySuccessActivity.Companion.KEY_SUCCESS_COUNT, number) + putExtra(RecoverySuccessActivity.Companion.KEY_SUCCESS_TYPE, type) + putExtra(Common.KEY_FILE_TYPE, fileType.value) + }) + } + + override fun onDestroy() { + super.onDestroy() + playMediaManager?.stopPlayAudio() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultActivity.kt deleted file mode 100644 index 4949f1e..0000000 --- a/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultActivity.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.ux.video.file.filerecovery.documents - -import android.view.LayoutInflater -import androidx.recyclerview.widget.LinearLayoutManager -import com.ux.video.file.filerecovery.base.BaseActivity -import com.ux.video.file.filerecovery.databinding.ActivityDocumentsScanResultBinding -import com.ux.video.file.filerecovery.utils.ScanRepository - -class DocumentsScanResultActivity : BaseActivity() { - - - private var resultAdapter: DocumentsScanResultAdapter? = null - override fun inflateBinding(inflater: LayoutInflater): ActivityDocumentsScanResultBinding = - ActivityDocumentsScanResultBinding.inflate(layoutInflater) - - override fun initView() { - super.initView() - resultAdapter = DocumentsScanResultAdapter(this@DocumentsScanResultActivity) - binding.recyclerView.run { - adapter = resultAdapter - layoutManager = LinearLayoutManager(this@DocumentsScanResultActivity) - } - } - - override fun initData() { - super.initData() -// ScanRepository.instance.photoResults.observe(this@DocumentsScanResultActivity) { -// resultAdapter?.setData(it) -// } - - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultAdapter.kt deleted file mode 100644 index 40773b7..0000000 --- a/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultAdapter.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.ux.video.file.filerecovery.documents - -import android.content.Context -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.db.ResultData - -class DocumentsScanResultAdapter(mContext: Context) : - BaseAdapter(mContext) { - override fun getViewBinding(parent: ViewGroup): DocumentsScanResultAdapterBinding = - DocumentsScanResultAdapterBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) - - override fun bindItem( - holder: VHolder, - item: ResultData - ) { - - holder.vb.run { - item.run { - tvDirName.text = dirName - tvFileCount.text = allFiles.size.toString() - } - - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/main/MainActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/main/MainActivity.kt index de8567c..afcf59e 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/main/MainActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/main/MainActivity.kt @@ -10,25 +10,25 @@ import android.os.Build import android.os.Environment import android.provider.Settings import android.view.LayoutInflater -import android.view.View -import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.core.net.toUri -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.recovery.RecoveryActivity -import com.ux.video.file.filerecovery.sort.DatePickerDialogFragment +import com.ux.video.file.filerecovery.settings.SetupActivity +import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.FileType import com.ux.video.file.filerecovery.utils.ScanManager class MainActivity : BaseActivity() { - private var dialogCustomerDateStart:DatePickerDialogFragment? = null - private var dialogPermission: PermissionDialogFragment? = null + private var pendingAction: NavigateAction? = null + private var dialogPermission: PermissionDialogFragment? = null + //是否正确引导用户打开所有文件管理权限 private var isRequestPermission = false - private var currentGoType = ScanSelectTypeActivity.Companion.VALUE_PHOTO + private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> var isAllOK = true @@ -54,47 +54,49 @@ class MainActivity : BaseActivity() { override fun initView() { super.initView() - onBackPressedDispatcher.addCallback( - this, - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - ScanManager.showLog("权限", "====0000000") - if (binding.layoutPermission.isVisible) { - ScanManager.showLog("权限", "====111111") - binding.layoutPermission.visibility = View.GONE - } else { - ScanManager.showLog("权限", "====222222222222") - finish() - } - } - }) +// onBackPressedDispatcher.addCallback( +// this, +// object : OnBackPressedCallback(true) { +// override fun handleOnBackPressed() { +// ScanManager.showLog("权限", "====0000000") +// if (binding.layoutPermission.isVisible) { +// ScanManager.showLog("权限", "====111111") +//// binding.layoutPermission.visibility = View.GONE +// } else { +// ScanManager.showLog("权限", "====222222222222") +// finish() +// } +// } +// }) } override fun initData() { super.initData() initTitleColor() binding.run { - allow.setOnClickListener { + imSetting.setOnClickListener { + startActivity(Intent(this@MainActivity, SetupActivity::class.java)) } layoutPhoto.setOnClickListener { - currentGoType = ScanSelectTypeActivity.Companion.VALUE_PHOTO + pendingAction = NavigateAction.Scan(FileType.PHOTO) intentCheck() } layoutVideo.setOnClickListener { - currentGoType = ScanSelectTypeActivity.Companion.VALUE_VIDEO + pendingAction = NavigateAction.Scan(FileType.VIDEO) intentCheck() } layoutAudio.setOnClickListener { - currentGoType = ScanSelectTypeActivity.Companion.VALUE_AUDIO + pendingAction = NavigateAction.Scan(FileType.AUDIO) intentCheck() } layoutDocument.setOnClickListener { - currentGoType = ScanSelectTypeActivity.Companion.VALUE_DOCUMENT + pendingAction = NavigateAction.Scan(FileType.DOCUMENT) intentCheck() } layoutRecovery.setOnClickListener { - startActivity(Intent(this@MainActivity,RecoveryActivity::class.java)) + pendingAction = NavigateAction.Recovered + intentCheck() } } binding.tvTitle.setOnClickListener { @@ -102,53 +104,6 @@ class MainActivity : BaseActivity() { showDatePicker() } - binding.btnScanAllPhoto.setOnClickListener { - - startActivity(Intent(this@MainActivity, ScanSelectTypeActivity::class.java).apply { - putExtra( - ScanSelectTypeActivity.Companion.KEY_FILE_TYPE, - ScanSelectTypeActivity.Companion.VALUE_PHOTO - ) - }) - } - binding.btnScanAllVideo.setOnClickListener { - - startActivity(Intent(this@MainActivity, ScanSelectTypeActivity::class.java).apply { - putExtra( - ScanSelectTypeActivity.Companion.KEY_FILE_TYPE, - ScanSelectTypeActivity.Companion.VALUE_VIDEO - ) - }) - } - binding.btnScanAllAudio.setOnClickListener { - - startActivity(Intent(this@MainActivity, ScanSelectTypeActivity::class.java).apply { - putExtra( - ScanSelectTypeActivity.Companion.KEY_FILE_TYPE, - ScanSelectTypeActivity.Companion.VALUE_AUDIO - ) - }) - } - - binding.btnScanAllFile.setOnClickListener { -// val results = mutableListOf() - val root = Environment.getExternalStorageDirectory() - -// ScanManager.scanAllDocuments(root, results) -// ScanRepository.instance.setResults(results) - - - - -// startActivity(Intent(this@MainActivity,DocumentsScanResultActivity::class.java)) -// -// results.forEach { doc -> -// ScanManager.showLog("FileScan", "目录: ${doc.dirName}, 文件数: ${doc.allFiles.size}") -// doc.allFiles.forEach { file -> -// ScanManager.showLog("FileScan", " -> 文件: ${file.targetFile.isHidden} ") -// } -// } - } } private fun initTitleColor() { @@ -158,7 +113,10 @@ class MainActivity : BaseActivity() { val width = paint.measureText(text.toString()) val shader = LinearGradient( 0f, 0f, width, textSize, - intArrayOf(getColor(R.color.color_title_start_color),getColor(R.color.color_title_end_color)), + intArrayOf( + getColor(R.color.color_title_start_color), + getColor(R.color.color_title_end_color) + ), null, Shader.TileMode.CLAMP ) @@ -177,7 +135,6 @@ class MainActivity : BaseActivity() { isRequestPermission = false ScanManager.showLog("--", "-------onResume") startScan() - binding.layoutPermission.visibility = View.GONE } } @@ -211,7 +168,7 @@ class MainActivity : BaseActivity() { private fun requestPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - dialogPermission = dialogPermission?: PermissionDialogFragment{ + dialogPermission = dialogPermission ?: PermissionDialogFragment { try { val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply { @@ -224,7 +181,7 @@ class MainActivity : BaseActivity() { } isRequestPermission = true } - dialogPermission?.show(supportFragmentManager,"") + dialogPermission?.show(supportFragmentManager, "") } else { requestPermissionLauncher.launch( arrayOf( @@ -236,14 +193,31 @@ class MainActivity : BaseActivity() { } private fun startScan() { - startActivity(Intent(this@MainActivity, ScanSelectTypeActivity::class.java).apply { - putExtra(ScanSelectTypeActivity.Companion.KEY_FILE_TYPE, currentGoType) - }) + + when (val action = pendingAction) { + is NavigateAction.Scan -> startActivity( + Intent( + this@MainActivity, + ScanSelectTypeActivity::class.java + ).apply { + putExtra(Common.KEY_FILE_TYPE, action.fileType.value) + }) + + NavigateAction.Recovered -> startActivity( + Intent( + this@MainActivity, + RecoveryActivity::class.java + ) + ) + + else -> Unit + } + pendingAction = null } override fun onDestroy() { super.onDestroy() - binding.layoutPermission.isVisible = false +// binding.layoutPermission.isVisible = false } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/main/NavigateAction.kt b/app/src/main/java/com/ux/video/file/filerecovery/main/NavigateAction.kt new file mode 100644 index 0000000..67476ff --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/main/NavigateAction.kt @@ -0,0 +1,8 @@ +package com.ux.video.file.filerecovery.main + +import com.ux.video.file.filerecovery.utils.FileType + +sealed class NavigateAction { + data class Scan(val fileType: FileType) : NavigateAction() + object Recovered : NavigateAction() +} diff --git a/app/src/main/java/com/ux/video/file/filerecovery/main/ScanSelectTypeActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/main/ScanSelectTypeActivity.kt index b4f1d3b..85a2fe7 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/main/ScanSelectTypeActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/main/ScanSelectTypeActivity.kt @@ -6,15 +6,9 @@ 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.result.ScanningActivity -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 -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video +import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.FileType +import com.ux.video.file.filerecovery.utils.ScanType import kotlin.properties.Delegates @@ -23,14 +17,6 @@ import kotlin.properties.Delegates */ class ScanSelectTypeActivity : BaseActivity() { - companion object { - val KEY_FILE_TYPE = "file_type" - val VALUE_PHOTO = 0 - val VALUE_VIDEO = 1 - val VALUE_AUDIO = 2 - val VALUE_DOCUMENT = 3 - } - private var allType by Delegates.notNull() private var deletedType by Delegates.notNull() override fun inflateBinding(inflater: LayoutInflater): ActivityScanSelectTypeBinding = @@ -39,8 +25,10 @@ class ScanSelectTypeActivity : BaseActivity() { override fun initView() { super.initView() - val type = intent.getIntExtra(KEY_FILE_TYPE, VALUE_PHOTO) - setSelectType(type) + val intExtra = intent.getIntExtra(Common.KEY_FILE_TYPE, FileType.PHOTO.value) + val fileType = FileType.from(intExtra) + fileType?.let { setSelectType(it) } + } @@ -50,40 +38,40 @@ class ScanSelectTypeActivity : BaseActivity() { binding.scanAllFile.setOnClickListener { startActivity(Intent(this@ScanSelectTypeActivity, ScanningActivity::class.java).apply { - putExtra(KEY_SCAN_TYPE, allType) + putExtra(Common.KEY_SCAN_TYPE, allType) }) } binding.scanDeletedFile.setOnClickListener { startActivity(Intent(this@ScanSelectTypeActivity, ScanningActivity::class.java).apply { - putExtra(KEY_SCAN_TYPE, deletedType) + putExtra(Common.KEY_SCAN_TYPE, deletedType) }) } } - private fun setSelectType(fileType: Int) { + private fun setSelectType(fileType: FileType) { when (fileType) { - VALUE_PHOTO -> { - allType = VALUE_SCAN_TYPE_photo - deletedType = VALUE_SCAN_TYPE_deleted_photo + FileType.PHOTO -> { + allType = ScanType.ALL_PHOTO.value + deletedType = ScanType.DELETED_PHOTO.value binding.title.text = getString(R.string.photo_title) } - VALUE_VIDEO -> { - allType = VALUE_SCAN_TYPE_video - deletedType = VALUE_SCAN_TYPE_deleted_video + FileType.VIDEO -> { + allType = ScanType.ALL_VIDEO.value + deletedType = ScanType.DELETED_VIDEO.value binding.title.text = getString(R.string.video_title) } - VALUE_AUDIO -> { - allType = VALUE_SCAN_TYPE_audio - deletedType = VALUE_SCAN_TYPE_deleted_audio + FileType.AUDIO -> { + allType = ScanType.ALL_AUDIO.value + deletedType = ScanType.DELETED_AUDIO.value binding.title.text = getString(R.string.audio_title) } - VALUE_DOCUMENT -> { - allType = VALUE_SCAN_TYPE_documents - deletedType = VALUE_SCAN_TYPE_deleted_documents + FileType.DOCUMENT -> { + allType = ScanType.ALL_DOCUMENT.value + deletedType = ScanType.DELETED_DOCUMENT.value binding.title.text = getString(R.string.document_title) } } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/RecoveryActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/RecoveryActivity.kt index f6f2ae6..2d3c8b5 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/recovery/RecoveryActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/recovery/RecoveryActivity.kt @@ -11,26 +11,32 @@ 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.recovery.ui.recoveryphoto.RecoveredFragment import com.ux.video.file.filerecovery.recovery.ui.recoveryphoto.RecoveryPhotoViewModel import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.CustomTextView +import com.ux.video.file.filerecovery.utils.FileType +import com.ux.video.file.filerecovery.utils.ScanManager import kotlinx.coroutines.launch class RecoveryActivity : BaseActivity() { val sharedViewModel: RecoveryPhotoViewModel by viewModels() + private lateinit var fileType: FileType + override fun inflateBinding(inflater: LayoutInflater): ActivityRecoveryBinding = ActivityRecoveryBinding.inflate(inflater) override fun initView() { super.initView() + val intExtra = intent.getIntExtra(Common.KEY_FILE_TYPE, FileType.PHOTO.value) + fileType = FileType.from(intExtra)!! binding.run { val fragments = listOf( - RecoveryPhotoFragment.newInstance(Common.FILE_TYPE_PHOTO), - RecoveryPhotoFragment.newInstance(Common.FILE_TYPE_VIDEO), - RecoveryPhotoFragment.newInstance(Common.FILE_TYPE_AUDIO), - RecoveryPhotoFragment.newInstance(Common.FILE_TYPE_DOCUMENTS) + RecoveredFragment.newInstance(FileType.PHOTO.value), + RecoveredFragment.newInstance(FileType.VIDEO.value), + RecoveredFragment.newInstance(FileType.AUDIO.value), + RecoveredFragment.newInstance(FileType.DOCUMENT.value) ) viewPage2.adapter = MyViewPage2Adapter(this@RecoveryActivity, fragments) TabLayoutMediator(tabLayout, viewPage2) { tab, position -> @@ -74,6 +80,17 @@ class RecoveryActivity : BaseActivity() { }) + ScanManager.showLog("-----tt-------", "-------fileType=${fileType}") + fileType.run { + when (this) { + FileType.PHOTO -> viewPage2.currentItem = 0 + FileType.VIDEO -> viewPage2.currentItem = 1 + FileType.AUDIO -> viewPage2.currentItem = 2 + FileType.DOCUMENT -> viewPage2.currentItem = 3 + } + } + + } } @@ -82,27 +99,48 @@ class RecoveryActivity : BaseActivity() { binding.imageBack.setOnClickListener { finish() } + sharedViewModel.photoFiles.observe(this) { + setTabCount( FileType.PHOTO, it.size) + } + sharedViewModel.videoFiles.observe(this) { + setTabCount( FileType.VIDEO, it.size) + } + sharedViewModel.audioFiles.observe(this) { + setTabCount( FileType.AUDIO, it.size) + } + sharedViewModel.documentsFiles.observe(this) { + setTabCount(FileType.DOCUMENT, it.size) + } lifecycleScope.launch { - val recoveryFilePhoto = ObjectBoxManager.queryRecoveryFileAsync(Common.FILE_TYPE_PHOTO) - sharedViewModel.setPhotosRecoveredFiles(recoveryFilePhoto) - val recoveryFileVideo = ObjectBoxManager.queryRecoveryFileAsync(Common.FILE_TYPE_VIDEO) - sharedViewModel.setVideosRecoveredFiles(recoveryFileVideo) - val recoveryFileAudio = ObjectBoxManager.queryRecoveryFileAsync(Common.FILE_TYPE_AUDIO) - sharedViewModel.setAudiosRecoveredFiles(recoveryFileAudio) - val recoveryFileDocuments = - ObjectBoxManager.queryRecoveryFileAsync(Common.FILE_TYPE_DOCUMENTS) - sharedViewModel.setDocumentsRecoveredFiles(recoveryFileDocuments) - binding.tabLayout.run { - getTabAt(0)?.customView?.findViewById(R.id.tab_item_count)?.text = - getString(R.string.text_counts, recoveryFilePhoto.size) - getTabAt(1)?.customView?.findViewById(R.id.tab_item_count)?.text = - getString(R.string.text_counts, recoveryFileVideo.size) - getTabAt(2)?.customView?.findViewById(R.id.tab_item_count)?.text = - getString(R.string.text_counts, recoveryFileAudio.size) - getTabAt(3)?.customView?.findViewById(R.id.tab_item_count)?.text = - getString(R.string.text_counts, recoveryFileDocuments.size) + FileType.entries.forEach{ fileType-> + val recoveryFilePhoto = ObjectBoxManager.queryRecoveryFileAsync(fileType) + sharedViewModel.setRecoveredFiles(fileType, recoveryFilePhoto) + setTabCount(fileType, recoveryFilePhoto.size) + } } } + + private fun setTabCount(fileType: FileType, number: Int) { + binding.tabLayout.run { + getTabAt(fileType.tabIndex)?.customView?.findViewById(R.id.tab_item_count)?.text = + getString(R.string.text_counts, number) +// when (fileType) { +// FileType.PHOTO -> getTabAt(0)?.customView?.findViewById(R.id.tab_item_count)?.text = +// getString(R.string.text_counts, number) +// +// FileType.VIDEO -> getTabAt(1)?.customView?.findViewById(R.id.tab_item_count)?.text = +// getString(R.string.text_counts, number) +// +// FileType.AUDIO -> getTabAt(2)?.customView?.findViewById(R.id.tab_item_count)?.text = +// getString(R.string.text_counts, number) +// +// FileType.DOCUMENT -> getTabAt(3)?.customView?.findViewById( +// R.id.tab_item_count +// )?.text = +// getString(R.string.text_counts, number) +// } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/DeleteSuccessDialog.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/DeleteSuccessDialog.kt new file mode 100644 index 0000000..33bce05 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/DeleteSuccessDialog.kt @@ -0,0 +1,43 @@ +package com.ux.video.file.filerecovery.recovery.ui.recoveryphoto + +import android.graphics.Color +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.graphics.drawable.toDrawable +import androidx.fragment.app.DialogFragment + +import com.ux.video.file.filerecovery.databinding.DialogDeleteSuccessBinding +import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx + +class DeleteSuccessDialog(): DialogFragment() { + private lateinit var binding: DialogDeleteSuccessBinding + override fun onStart() { + super.onStart() + dialog?.setCanceledOnTouchOutside(false) + isCancelable = false + dialog?.window?.apply { + setLayout( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + val params = attributes + params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL + params.y = 100.dpToPx(context) + attributes = params + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + + binding = DialogDeleteSuccessBinding.inflate(inflater) + return binding.root + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveredFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveredFragment.kt new file mode 100644 index 0000000..170ffdf --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveredFragment.kt @@ -0,0 +1,297 @@ +package com.ux.video.file.filerecovery.recovery.ui.recoveryphoto + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.view.isVisible +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import com.ux.video.file.filerecovery.R +import com.ux.video.file.filerecovery.base.BaseFragment +import com.ux.video.file.filerecovery.databinding.FragmentRecoveryPhotoBinding +import com.ux.video.file.filerecovery.db.ObjectBoxManager + +import com.ux.video.file.filerecovery.db.ResultDataFiles +import com.ux.video.file.filerecovery.db.targetFile +import com.ux.video.file.filerecovery.detail.DetailsActivity +import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity +import com.ux.video.file.filerecovery.sort.SortDisplayChildAdapter +import com.ux.video.file.filerecovery.sort.RecoverOrDeleteManager +import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE +import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx +import com.ux.video.file.filerecovery.utils.FileType + + +class RecoveredFragment : BaseFragment() { + + companion object { + private const val ARG_FILE_TYPE = "arg_file_type" + + fun newInstance(fileType: Int): RecoveredFragment { + val fragment = RecoveredFragment() + val args = Bundle().apply { + putInt(ARG_FILE_TYPE, fileType) + } + fragment.arguments = args + return fragment + } + } + + + private var fileType: FileType? = null + + private var mAdapter: SortDisplayChildAdapter? = null + private var selectedList: Set? = null + + + private val sharedViewModel: RecoveryPhotoViewModel by activityViewModels() + private lateinit var recoveredList: List + + + override fun initBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentRecoveryPhotoBinding = + FragmentRecoveryPhotoBinding.inflate(inflater, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val i = arguments?.getInt(ARG_FILE_TYPE) ?: FileType.PHOTO.value + fileType = FileType.from(i) + binding.run { + fileType?.let { + when (it) { + FileType.PHOTO -> { + layoutEmpty.tvScan.text = getString(R.string.scan_photos) + sharedViewModel.photoFiles.observe(viewLifecycleOwner) { + initViewVisible(it) + setRecoveredData(it, FileType.PHOTO) + } + sharedViewModel.selectedPhoto.observe(viewLifecycleOwner) { displaySet -> + selectedList = displaySet + Common.showLog("当前显示筛选数据 选中状态更新: ${displaySet.size}") + selectedList?.let { + updateSelectStatus(it.size) + } + + } + } + + FileType.VIDEO -> { + layoutEmpty.tvScan.text = getString(R.string.scan_videos) + sharedViewModel.videoFiles.observe(viewLifecycleOwner) { + initViewVisible(it) + setRecoveredData(it, FileType.VIDEO ) + } + sharedViewModel.selectedVideo.observe(viewLifecycleOwner) { displaySet -> + selectedList = displaySet + Common.showLog("当前显示筛选数据 选中状态更新: ${displaySet.size}") + selectedList?.let { + updateSelectStatus(it.size) + } + } + } + + FileType.AUDIO -> { + layoutEmpty.tvScan.text = getString(R.string.scan_audios) + sharedViewModel.audioFiles.observe(viewLifecycleOwner) { + initViewVisible(it) + setRecoveredData(it, FileType.AUDIO ) + } + sharedViewModel.selectedAudio.observe(viewLifecycleOwner) { displaySet -> + selectedList = displaySet + Common.showLog("当前显示筛选数据 选中状态更新: ${displaySet.size}") + selectedList?.let { + updateSelectStatus(it.size) + } + } + } + + FileType.DOCUMENT -> { + layoutEmpty.tvScan.text = getString(R.string.scan_documents) + sharedViewModel.documentsFiles.observe(viewLifecycleOwner) { + initViewVisible(it) + setRecoveredData(it, FileType.DOCUMENT) + } + sharedViewModel.selectedDocuments.observe(viewLifecycleOwner) { displaySet -> + selectedList = displaySet + Common.showLog("当前显示筛选数据 选中状态更新: ${displaySet.size}") + selectedList?.let { + updateSelectStatus(it.size) + } + } + } + + } + } + } + updateSelectStatus(0) + initClick() + + } + + + private fun initViewVisible(list: List) { + recoveredList = list + binding.run { + recyclerView.isVisible = list.isNotEmpty() + layoutEmpty.relativeEmptyMain.isVisible = !list.isNotEmpty() + layoutBottom.run { + bottomMainLayout.isVisible = list.isNotEmpty() +// tvRight.text = getString(R.string.share) + } + tvAllSelect.isVisible = list.isNotEmpty() + } + } + + private fun updateSelectStatus(selectedCounts: Int) { + binding.run { + selectedCounts.let { + tvAllSelect.isSelected = it == mAdapter?.itemCount + if (it <= 0) { + layoutBottom.tvLeft.isEnabled = false + layoutBottom.tvRight.isEnabled = false + } else { + layoutBottom.tvLeft.isEnabled = true + layoutBottom.tvRight.isEnabled = true + } + layoutBottom.tvLeft.text = getString(R.string.delete_placeholder, it) + layoutBottom.tvRight.text = getString(R.string.share_placeholder, it) + + } + } + + } + + private fun setRecoveredData(list: List, fileType: FileType) { + + mAdapter = SortDisplayChildAdapter( + mContext = requireContext(), + fileType = fileType, + mColumns = 2, + viewModel = sharedViewModel, + onSelectedUpdate = { resultPhotosFiles, isAdd, allSelected -> + fileType?.let { sharedViewModel.toggleSelection(it, isAdd, resultPhotosFiles) } + + + }, + hideThumbnailsUpdate = { hide -> + + }) { item -> + + startActivity( + Intent( + requireContext(), + DetailsActivity::class.java + ).apply { + putExtra(KEY_SCAN_TYPE, fileType.value) + putExtra(DetailsActivity.KEY_CLICK_ITEM, item) + putExtra(DetailsActivity.KEY_SHOW_SHARE, true) + }) + + }.apply { + setData(list) + } + + binding.recyclerView.run { + adapter = mAdapter + when (fileType) { + FileType.PHOTO, FileType.VIDEO -> { + layoutManager = GridLayoutManager(requireContext(), 2) + val bPx = 6.dpToPx(context) + val aPx = 16.dpToPx(context) + val bottom = 75.dpToPx(context) + setPadding(aPx, 0, bPx, bottom) + } + + + FileType.AUDIO , FileType.DOCUMENT -> { + layoutManager = LinearLayoutManager(requireContext()) + val bPx = 6.dpToPx(context) + val aPx = 16.dpToPx(context) + val bottom = 75.dpToPx(context) + setPadding(aPx, 0, bPx, bottom) + } + + } + + } + + } + + private fun initClick() { + binding.run { + + fileType?.let { fileType -> + layoutEmpty.tvScan.setOnClickListener { + startActivity( + Intent( + requireActivity(), + ScanSelectTypeActivity::class.java + ).apply { + putExtra(Common.KEY_FILE_TYPE, fileType.value) + }) + requireActivity().finish() + } + tvAllSelect.setOnClickListener { + it.isSelected = !it.isSelected + mAdapter?.setAllSelected(it.isSelected) + + } + layoutBottom.tvLeft.setOnClickListener { + selectedList?.let { + RecoverOrDeleteManager.showConfirmDeleteDialog( + fragmentManager = requireActivity().supportFragmentManager, + scope = lifecycleScope, + selectedSetList = it + ) { count -> + val removeSelectedFromSizeList = + sharedViewModel.removeSelectedFromSizeList(recoveredList, it) + ObjectBoxManager.deleteRecoveryFilesAsyncById( + lifecycleScope, + it.toList() + ) {} + sharedViewModel.setRecoveredFiles(fileType, removeSelectedFromSizeList) + sharedViewModel.clearSelected(fileType) + showCustomToast(requireContext()) + } + } + } + + layoutBottom.tvRight.setOnClickListener { + selectedList?.let { list-> + if (list.size == 1) { + list.first().targetFile().let { file -> + Common.shareSingleFile(requireActivity(), file, fileType) + } + } else if (list.isNotEmpty()) { + val toList = list.map { it.targetFile() }.toList() + Common.shareMultipleFiles(requireActivity(), toList, fileType) + } + + } + } + } + } + + + } + + fun showCustomToast(context: Context) { + val inflater = LayoutInflater.from(context) + Toast(context).apply { + duration = Toast.LENGTH_SHORT + this.view = inflater.inflate(R.layout.dialog_delete_success, null) + setGravity(Gravity.CENTER_HORIZONTAL or Gravity.TOP, 0, 150.dpToPx(context)) + show() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoFragment.kt deleted file mode 100644 index 1bc7b75..0000000 --- a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoFragment.kt +++ /dev/null @@ -1,192 +0,0 @@ -package com.ux.video.file.filerecovery.recovery.ui.recoveryphoto - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.ux.video.file.filerecovery.R -import com.ux.video.file.filerecovery.base.BaseFragment -import com.ux.video.file.filerecovery.databinding.FragmentRecoveryPhotoBinding -import com.ux.video.file.filerecovery.db.ResultDataFiles -import com.ux.video.file.filerecovery.sort.PhotoDisplayDateAdapter -import com.ux.video.file.filerecovery.sort.PhotoDisplayDateChildAdapter -import com.ux.video.file.filerecovery.utils.Common -import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx - - -class RecoveryPhotoFragment : BaseFragment() { - - companion object { - private const val ARG_FILE_TYPE = "arg_file_type" - - fun newInstance(fileType: Int): RecoveryPhotoFragment { - val fragment = RecoveryPhotoFragment() - val args = Bundle().apply { - putInt(ARG_FILE_TYPE, fileType) - } - fragment.arguments = args - return fragment - } - } - - // 0-3 photo\video\audio\documents - private var fileType: Int = Common.FILE_TYPE_PHOTO - - private lateinit var mAdapter: PhotoDisplayDateChildAdapter - private var selectedList: List? = null - - - private val sharedViewModel: RecoveryPhotoViewModel by activityViewModels() - - override fun initBinding( - inflater: LayoutInflater, - container: ViewGroup? - ): FragmentRecoveryPhotoBinding = - FragmentRecoveryPhotoBinding.inflate(inflater, container, false) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - fileType = arguments?.getInt(ARG_FILE_TYPE) ?: Common.FILE_TYPE_PHOTO - - - binding.run { - when (fileType) { - Common.FILE_TYPE_PHOTO -> { - layoutEmpty.tvScan.text = getString(R.string.scan_photos) - sharedViewModel.photoFiles.observe(viewLifecycleOwner) { - initViewVisible(it) - setRecoveredData(it, Common.VALUE_SCAN_TYPE_photo) - - } - } - - Common.FILE_TYPE_VIDEO -> { - layoutEmpty.tvScan.text = getString(R.string.scan_videos) - sharedViewModel.videoFiles.observe(viewLifecycleOwner) { - initViewVisible(it) - setRecoveredData(it, Common.VALUE_SCAN_TYPE_video) - } - } - - Common.FILE_TYPE_AUDIO -> { - layoutEmpty.tvScan.text = getString(R.string.scan_audios) - sharedViewModel.audioFiles.observe(viewLifecycleOwner) { - initViewVisible(it) - setRecoveredData(it, Common.VALUE_SCAN_TYPE_audio) - } - } - - Common.FILE_TYPE_DOCUMENTS -> { - layoutEmpty.tvScan.text = getString(R.string.scan_documents) - sharedViewModel.documentsFiles.observe(viewLifecycleOwner) { - initViewVisible(it) - setRecoveredData(it, Common.VALUE_SCAN_TYPE_documents) - } - } - - } - } - sharedViewModel.selectedLiveData.observe(viewLifecycleOwner) { displaySet -> - selectedList = displaySet.toList() - Common.showLog("当前显示筛选数据 选中状态更新: ${displaySet.size}") - selectedList?.let { - updateSelectStatus(it.size) - } - - } - - initClick() - - } - - - private fun initViewVisible(list: List) { - binding.run { - recyclerView.isVisible = list.isNotEmpty() - layoutEmpty.relativeEmptyMain.isVisible = !list.isNotEmpty() - layoutBottom.run { - bottomMainLayout.isVisible = list.isNotEmpty() -// tvRight.text = getString(R.string.share) - } - tvAllSelect.isVisible = list.isNotEmpty() - } - } - - private fun updateSelectStatus(selectedCounts: Int) { - binding.run { - selectedCounts.let { - tvAllSelect.isSelected = it == mAdapter.itemCount - if (it <= 0) { - layoutBottom.tvLeft.isEnabled = false - layoutBottom.tvRight.isEnabled = false - } else { - layoutBottom.tvLeft.isEnabled = true - layoutBottom.tvRight.isEnabled = true - } - layoutBottom.tvLeft.text = getString(R.string.delete_placeholder, it) - layoutBottom.tvRight.text = getString(R.string.share_placeholder, it) - - } - } - - } - - private fun setRecoveredData(list: List, scanTye: Int) { - - mAdapter = PhotoDisplayDateChildAdapter( - mContext = requireContext(), - scanType = scanTye, - mColumns = 2, - viewModel = sharedViewModel, - onSelectedUpdate = { resultPhotosFiles, isAdd, allSelected -> - sharedViewModel.toggleSelection(isAdd, resultPhotosFiles) - - }, - hideThumbnailsUpdate = { hide -> - - }) { item -> - - }.apply { - setData(list) - } - updateSelectStatus(0) - binding.recyclerView.run { - adapter = mAdapter - when (scanTye) { - Common.VALUE_SCAN_TYPE_photo, Common.VALUE_SCAN_TYPE_video -> { - layoutManager = GridLayoutManager(requireContext(), 2) - val bPx = 6.dpToPx(context) - val aPx = 16.dpToPx(context) - val bottom = 75.dpToPx(context) - setPadding(aPx, 0, bPx, bottom) - } - - - Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_documents -> { - layoutManager = LinearLayoutManager(requireContext()) - val bPx = 6.dpToPx(context) - val aPx = 16.dpToPx(context) - val bottom = 75.dpToPx(context) - setPadding(aPx, 0, bPx, bottom) - } - - } - - } - - } - - private fun initClick() { - binding.tvAllSelect.setOnClickListener { - it.isSelected = !it.isSelected - mAdapter.setAllSelected(it.isSelected) - - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoViewModel.kt b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoViewModel.kt index f1a18a6..44a72f5 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoViewModel.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/recovery/ui/recoveryphoto/RecoveryPhotoViewModel.kt @@ -5,50 +5,102 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.ux.video.file.filerecovery.db.ResultDataFiles import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.FileType +/** + * + * RecoveryActivity RecoveredFragment 共用 + */ class RecoveryPhotoViewModel : ViewModel() { - private val _photoFiles = MutableLiveData< List>(emptyList()) + + //用于及时更新tab 显示恢复文件的数量 + private val _photoFiles = MutableLiveData>(emptyList()) val photoFiles: LiveData> = _photoFiles - private val _videoFiles = MutableLiveData< List>(emptyList()) - val videoFiles: LiveData< List> = _videoFiles + private val _videoFiles = MutableLiveData>(emptyList()) + val videoFiles: LiveData> = _videoFiles - private val _audioFiles = MutableLiveData< List>(emptyList()) - val audioFiles: LiveData< List> = _audioFiles + private val _audioFiles = MutableLiveData>(emptyList()) + val audioFiles: LiveData> = _audioFiles - private val _documentsFiles = MutableLiveData< List>(emptyList()) - val documentsFiles: LiveData< List> = _documentsFiles + private val _documentsFiles = MutableLiveData>(emptyList()) + val documentsFiles: LiveData> = _documentsFiles - private val _selectedLiveData = MutableLiveData>(emptySet()) - val selectedLiveData: LiveData> = _selectedLiveData - fun setPhotosRecoveredFiles(photos:List){ - _photoFiles.value = photos - } - fun setVideosRecoveredFiles(videos:List){ - _videoFiles.value = videos - } - fun setAudiosRecoveredFiles(audios:List){ - _audioFiles.value = audios - } - fun setDocumentsRecoveredFiles(documents:List){ - _documentsFiles.value = documents - } + private val _selectedPhotoLiveData = MutableLiveData>(emptySet()) + val selectedPhoto: LiveData> = _selectedPhotoLiveData - fun toggleSelection(isAdd: Boolean, resultDataFiles: ResultDataFiles) { - val current = _selectedLiveData.value?.toMutableSet() ?: mutableSetOf() - resultDataFiles.let { - if (isAdd) { - current.add(it) - } else { - current.remove(it) - } + private val _selectedVideoLiveData = MutableLiveData>(emptySet()) + val selectedVideo: LiveData> = _selectedVideoLiveData + + private val _selectedAudioLiveData = MutableLiveData>(emptySet()) + val selectedAudio: LiveData> = _selectedAudioLiveData + + private val _selectedDocumentsLiveData = MutableLiveData>(emptySet()) + val selectedDocuments: LiveData> = _selectedDocumentsLiveData + + + fun setRecoveredFiles(fileType: FileType, data: List) { + when (fileType) { + FileType.PHOTO -> _photoFiles.value = data + FileType.VIDEO -> _videoFiles.value = data + FileType.AUDIO -> _audioFiles.value = data + FileType.DOCUMENT -> _documentsFiles.value = data } - _selectedLiveData.value = current.toSet() + } - fun checkIsSelect(resultDataFiles: ResultDataFiles): Boolean { - val current = _selectedLiveData.value - return current?.contains(resultDataFiles) == true + + fun toggleSelection(fileType: FileType, isAdd: Boolean, resultDataFiles: ResultDataFiles) { + getSelectedList(fileType).let { selectList -> + val current = selectList.value?.toMutableSet() ?: mutableSetOf() + resultDataFiles.let { + if (isAdd) { + current.add(it) + } else { + current.remove(it) + } + } + selectList.value = current.toSet() + } + + } + + fun clearSelected(fileType: FileType) { + getSelectedList(fileType).let { + it.value = it.value?.toMutableSet()?.apply { + clear() + } ?: emptySet() + + } + } + + fun removeSelectedFromSizeList( + list: List, + selectedLiveData: Set + ): List { + return list.filterNot { it in selectedLiveData } + } + + + fun checkIsSelect(fileType: FileType, resultDataFiles: ResultDataFiles): Boolean { + return when (fileType) { + FileType.PHOTO -> _selectedPhotoLiveData + FileType.VIDEO-> _selectedVideoLiveData + FileType.AUDIO -> _selectedAudioLiveData + FileType.DOCUMENT -> _selectedDocumentsLiveData + }.let { + val current = it.value + current?.contains(resultDataFiles) == true + } + } + + private fun getSelectedList(fileType: FileType): MutableLiveData> { + return when (fileType) { + FileType.PHOTO -> _selectedPhotoLiveData + FileType.VIDEO -> _selectedVideoLiveData + FileType.AUDIO -> _selectedAudioLiveData + FileType.DOCUMENT -> _selectedDocumentsLiveData + } } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt index 48f83bf..3a2eaad 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt @@ -3,48 +3,37 @@ package com.ux.video.file.filerecovery.result import android.content.Intent import android.view.LayoutInflater import androidx.activity.OnBackPressedCallback +import androidx.activity.viewModels 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.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 -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video -import com.ux.video.file.filerecovery.utils.ExtendFunctions.getParcelableArrayListExtraCompat +import com.ux.video.file.filerecovery.sort.SortingActivity +import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.FileType +import com.ux.video.file.filerecovery.utils.ScanType /** * 扫描结果汇总展示 */ class ScanResultDisplayActivity : BaseActivity() { - private var scanType: Int = VALUE_SCAN_TYPE_photo + private lateinit var scanType: ScanType private var exitDialog: ExitDialogFragment? = null - private var list: ArrayList? = null + private lateinit var list: List + private val viewModel: ScanningResultDisplayViewModel by viewModels() - companion object { - val KEY_SCAN_RESULT = "scan_result" - - } override fun inflateBinding(inflater: LayoutInflater): ActivityScanResultDisplayBinding = ActivityScanResultDisplayBinding.inflate(inflater) override fun initView() { super.initView() - list = intent.getParcelableArrayListExtraCompat(KEY_SCAN_RESULT) - scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) - setSelectTypeTitle(scanType) - - + val intExtra = intent.getIntExtra(Common.KEY_SCAN_TYPE, ScanType.ALL_PHOTO.value) + scanType = ScanType.from(intExtra)!! + setSelectTypeTitle(scanType.mediaType) } override fun initData() { @@ -59,12 +48,12 @@ class ScanResultDisplayActivity : BaseActivity }) binding.run { - val myAdapter = when (scanType) { - VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { + val myAdapter = when (scanType.mediaType) { + FileType.AUDIO, FileType.DOCUMENT -> { bottomLayout.setBackgroundResource(R.drawable.bg_rectangle_white_top_20) - ScanResultDocumentsAdapter( + ScanResultDocumentsAudioAdapter( this@ScanResultDisplayActivity, - scanType + scanType.mediaType ) { folderLists -> goSort(folderLists) } @@ -72,60 +61,56 @@ class ScanResultDisplayActivity : BaseActivity else -> { bottomLayout.setBackgroundResource(0) - ScanResultPhotoAdapter( + ScanResultPhotoVideoAdapter( this@ScanResultDisplayActivity, - scanType + scanType.mediaType ) { folderLists -> goSort(folderLists) } } - - - }.apply { - list?.let { - textDirCount.text = it.size.toString() - val sumOf = it.sumOf { it.allFiles.size } + } + viewModel.scanData.observe(this@ScanResultDisplayActivity){ data-> + list = data + list.let { data -> + textDirCount.text = data.size.toString() + val sumOf = data.sumOf { it.allFiles.size } textAllCounts.text = sumOf.toString() - setData(it) + myAdapter.setData(data) } } recyclerResult.run { adapter = myAdapter layoutManager = LinearLayoutManager(this@ScanResultDisplayActivity) } - - } } - - private fun dealExit() { - exitDialog = exitDialog ?: ExitDialogFragment() { + exitDialog = exitDialog ?: ExitDialogFragment { finish() } exitDialog?.show(supportFragmentManager, "") } - private fun setSelectTypeTitle(fileType: Int) { + private fun setSelectTypeTitle(fileType: FileType) { binding.run { when (fileType) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { + FileType.PHOTO -> { title.text = getString(R.string.photo_title) textFileType.text = getString(R.string.text_photos) } - VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> { + FileType.VIDEO-> { title.text = getString(R.string.video_title) textFileType.text = getString(R.string.text_videos) } - VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> { + FileType.AUDIO -> { title.text = getString(R.string.audio_title) textFileType.text = getString(R.string.text_audios) } - VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { + FileType.DOCUMENT -> { title.text = getString(R.string.document_title) textFileType.text = getString(R.string.text_documents) } @@ -137,16 +122,13 @@ class ScanResultDisplayActivity : BaseActivity private fun goSort(list: ArrayList) { + viewModel.updateSortData(list) startActivity( Intent( this@ScanResultDisplayActivity, - PhotoSortingActivity::class.java + SortingActivity::class.java ).apply { - putExtra(KEY_SCAN_TYPE, scanType) - putParcelableArrayListExtra( - PhotoSortingActivity.KEY_PHOTO_FOLDER_FILE, - list - ) + putExtra(Common.KEY_FILE_TYPE, scanType.mediaType.value) }) } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAudioAdapter.kt similarity index 85% rename from app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAdapter.kt rename to app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAudioAdapter.kt index e24d83e..b98a272 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDocumentsAudioAdapter.kt @@ -10,13 +10,14 @@ import com.ux.video.file.filerecovery.databinding.ScanResultDocumentsAdapterBind 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.FileType /** * 文件或者音频的扫描结果汇总适配器 */ -class ScanResultDocumentsAdapter( +class ScanResultDocumentsAudioAdapter( mContext: Context, - var type: Int, + var fileType: FileType, var onClickItem: (allFiles: ArrayList) -> Unit ) : BaseAdapter(mContext) { @@ -38,13 +39,15 @@ class ScanResultDocumentsAdapter( relativeLayout.setOnClickListener { onClickItem(allFiles) } textDirName.text = dirName textFileCounts.text = allFiles.size.toString() - when(type){ - Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio->{ + when(fileType){ + FileType.AUDIO ->{ icon.setImageResource(R.drawable.icon_folder_audio) } - Common.VALUE_SCAN_TYPE_documents, Common.VALUE_SCAN_TYPE_deleted_documents->{ + FileType.DOCUMENT->{ icon.setImageResource(R.drawable.icon_folder_documents) } + + else -> Unit } } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoVideoAdapter.kt similarity index 80% rename from app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoAdapter.kt rename to app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoVideoAdapter.kt index 9619eaf..2d84b98 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultPhotoVideoAdapter.kt @@ -2,6 +2,7 @@ package com.ux.video.file.filerecovery.result import android.annotation.SuppressLint import android.content.Context +import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView @@ -9,16 +10,23 @@ 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.db.ResultData import com.ux.video.file.filerecovery.db.ResultDataFiles +import com.ux.video.file.filerecovery.db.targetFile import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx +import com.ux.video.file.filerecovery.utils.FileType +import com.ux.video.file.filerecovery.utils.ScanManager import java.io.File -class ScanResultPhotoAdapter( +/** + * 图片或者视频的扫描结果汇总适配器 + */ +class ScanResultPhotoVideoAdapter( mContext: Context, - var type: Int, + var fileType: FileType, var onClickItem: (allFiles: ArrayList) -> Unit ) : BaseAdapter(mContext) { @@ -45,7 +53,7 @@ class ScanResultPhotoAdapter( val takeFiles = allFiles.take(3) imageViews.forEachIndexed { index, imageView -> if (index < takeFiles.size) { - takeFiles[index].targetFile?.let { + takeFiles[index].targetFile().let { loadImageView(mContext, it, imageView) } } else { @@ -61,12 +69,17 @@ class ScanResultPhotoAdapter( } private fun loadImageView(context: Context, file: File, imageView: ImageView) { + ScanManager.showLog( + "加载图片", + "-----loadImageView--path = ${file.path}" + ) Glide.with(context) .load(file) .apply( RequestOptions() .transform(CenterCrop(), RoundedCorners(8.dpToPx(context))) ) + .error(R.drawable.photo_place_holder) .into(imageView) } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultRepository.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultRepository.kt new file mode 100644 index 0000000..a1bab7b --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultRepository.kt @@ -0,0 +1,24 @@ +package com.ux.video.file.filerecovery.result + +import androidx.lifecycle.MutableLiveData +import com.ux.video.file.filerecovery.db.ResultData +import com.ux.video.file.filerecovery.db.ResultDataFiles + +object ScanResultRepository { + + + //扫描数据 + val scanData = MutableLiveData>() + + + //扫描用于排序显示的数据 + val sortingData = MutableLiveData>() + + fun setScanResult(data: List) { + scanData.postValue(data) + } + + fun setSortResult(data: List) { + sortingData.postValue(data) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivity.kt index 3f29c7d..d439bb4 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivity.kt @@ -4,103 +4,83 @@ import android.annotation.SuppressLint import android.content.Intent import android.os.Environment import android.view.LayoutInflater +import androidx.activity.viewModels import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.databinding.ActivityScanningBinding -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_AUDIO -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_DOCUMENT -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_PHOTO -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_VIDEO -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 -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video +import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.FileType import com.ux.video.file.filerecovery.utils.ScanManager -import com.ux.video.file.filerecovery.utils.ScanRepository import com.ux.video.file.filerecovery.utils.ScanState +import com.ux.video.file.filerecovery.utils.ScanType import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch -import androidx.lifecycle.repeatOnLifecycle class ScanningActivity : BaseActivity() { - - private var scanType: Int = VALUE_SCAN_TYPE_photo + private val viewModel: ScanningActivityViewModel by viewModels() + private lateinit var scanType: ScanType override fun inflateBinding(inflater: LayoutInflater): ActivityScanningBinding = ActivityScanningBinding.inflate(inflater) override fun initData() { super.initData() - scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) + val typeValue = intent.getIntExtra(Common.KEY_SCAN_TYPE, ScanType.ALL_PHOTO.value) + scanType = ScanType.from(typeValue)!! setSelectType(scanType) - when (scanType) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_documents -> scanAll() - VALUE_SCAN_TYPE_deleted_photo, VALUE_SCAN_TYPE_deleted_video, VALUE_SCAN_TYPE_deleted_audio, VALUE_SCAN_TYPE_deleted_documents -> scanDeleted() + if (scanType.isDeletedScan) { + scanDeleted() + } else { + scanAll() } - binding.imageViewBack.setOnClickListener { finish() } } - private fun setSelectType(fileType: Int) { + private fun setSelectType(scanType: ScanType) { binding.run { - - when (fileType) { - VALUE_SCAN_TYPE_photo -> { + when (scanType.mediaType) { + FileType.PHOTO->{ title.text = getString(R.string.photo_title) - tvScanDescribe.text = getString(R.string.describe_photos) scanProgress.setCenterImage(R.drawable.im_photo_center_image) + if(scanType.isDeletedScan){ + tvScanDescribe.text = getString(R.string.describe_delete_photos) + }else{ + tvScanDescribe.text = getString(R.string.describe_photos) + } } - - VALUE_SCAN_TYPE_deleted_photo -> { - title.text = getString(R.string.photo_title) - tvScanDescribe.text = getString(R.string.describe_delete_photos) - scanProgress.setCenterImage(R.drawable.im_photo_center_image) - } - - VALUE_SCAN_TYPE_video -> { + FileType.VIDEO->{ title.text = getString(R.string.video_title) - tvScanDescribe.text = getString(R.string.describe_videos) scanProgress.setCenterImage(R.drawable.im_video_center_image) + if(scanType.isDeletedScan){ + tvScanDescribe.text = getString(R.string.describe_delete_videos) + }else{ + tvScanDescribe.text = getString(R.string.describe_videos) + } } - VALUE_SCAN_TYPE_deleted_video -> { - title.text = getString(R.string.video_title) - tvScanDescribe.text = getString(R.string.describe_delete_videos) - scanProgress.setCenterImage(R.drawable.im_video_center_image) - } - - VALUE_SCAN_TYPE_audio -> { + FileType.AUDIO->{ title.text = getString(R.string.audio_title) - tvScanDescribe.text = getString(R.string.describe_audios) scanProgress.setCenterImage(R.drawable.im_audio_center_image) + if(scanType.isDeletedScan){ + tvScanDescribe.text = getString(R.string.describe_delete_audios) + }else{ + tvScanDescribe.text = getString(R.string.describe_audios) + } } - - VALUE_SCAN_TYPE_deleted_audio -> { - title.text = getString(R.string.audio_title) - tvScanDescribe.text = getString(R.string.describe_delete_audios) - scanProgress.setCenterImage(R.drawable.im_audio_center_image) - } - - VALUE_SCAN_TYPE_documents -> { + FileType.DOCUMENT->{ title.text = getString(R.string.document_title) - tvScanDescribe.text = getString(R.string.describe_documents) - scanProgress.setCenterImage(R.drawable.im_documents_center_image) - } - - VALUE_SCAN_TYPE_deleted_documents -> { - title.text = getString(R.string.document_title) - tvScanDescribe.text = getString(R.string.describe_delete_documents) scanProgress.setCenterImage(R.drawable.im_documents_center_image) + if(scanType.isDeletedScan){ + tvScanDescribe.text = getString(R.string.describe_delete_documents) + }else{ + tvScanDescribe.text = getString(R.string.describe_documents) + } } } } @@ -113,21 +93,19 @@ class ScanningActivity : BaseActivity() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { val root = Environment.getExternalStorageDirectory() - ScanManager.scanAllDocuments(this@ScanningActivity, root, type = scanType) + ScanManager.scanAllDocuments(this@ScanningActivity, root, fileType = scanType.mediaType) .flowOn(Dispatchers.IO).collect { - when (it) { - is ScanState.Progress -> { - updateProgress(it) - } + when (it) { + is ScanState.Progress -> { + updateProgress(it) + } - is ScanState.Complete -> { - updateComplete(it) + is ScanState.Complete -> { + updateComplete(it) + } } } - } } - - } } @@ -135,19 +113,19 @@ class ScanningActivity : BaseActivity() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { val root = Environment.getExternalStorageDirectory() - ScanManager.scanHiddenPhotoAsync(this@ScanningActivity, root, type = scanType) + ScanManager.scanHiddenPhotoAsync(this@ScanningActivity, root, fileType = scanType.mediaType) .flowOn(Dispatchers.IO).collect { - when (it) { - is ScanState.Progress -> { - updateProgress(it) + when (it) { + is ScanState.Progress -> { + updateProgress(it) - } + } - is ScanState.Complete -> { - updateComplete(it) + is ScanState.Complete -> { + updateComplete(it) + } } } - } } } @@ -182,18 +160,19 @@ class ScanningActivity : BaseActivity() { if (size == 0) { tvScanDescribe.text.let { tvEmptyTypeFile.text = "0 $it" - tvSorry.text = getString(R.string.not_found,it) + tvSorry.text = getString(R.string.not_found, it) } relativeScanFinishedEmpty.isVisible = true linearCounts.isVisible = false - }else{ + } else { finish() - startActivity(Intent(this@ScanningActivity, ScanResultDisplayActivity::class.java).apply { - putParcelableArrayListExtra( - ScanResultDisplayActivity.KEY_SCAN_RESULT, - it.result - ) - putExtra(KEY_SCAN_TYPE, scanType) + viewModel.updateData(it.result) + startActivity( + Intent( + this@ScanningActivity, + ScanResultDisplayActivity::class.java + ).apply { + putExtra(Common.KEY_SCAN_TYPE, scanType.value) }) } ScanManager.showLog("HiddenScan", "完成: ${it.result.size}") diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivityViewModel.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivityViewModel.kt new file mode 100644 index 0000000..3a32e46 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivityViewModel.kt @@ -0,0 +1,16 @@ +package com.ux.video.file.filerecovery.result + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.ux.video.file.filerecovery.db.ResultData + +class ScanningActivityViewModel : ViewModel() { + + + val scanData: MutableLiveData> = ScanResultRepository.scanData + + fun updateData(data: List) { + ScanResultRepository.setScanResult(data) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningResultDisplayViewModel.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningResultDisplayViewModel.kt new file mode 100644 index 0000000..c3961fa --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningResultDisplayViewModel.kt @@ -0,0 +1,19 @@ +package com.ux.video.file.filerecovery.result + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.ux.video.file.filerecovery.db.ResultData +import com.ux.video.file.filerecovery.db.ResultDataFiles + +class ScanningResultDisplayViewModel : ViewModel() { + + + val scanData: MutableLiveData> = ScanResultRepository.scanData + + fun updateData(data: List) { + ScanResultRepository.setScanResult(data) + } + fun updateSortData(data: List) { + ScanResultRepository.setSortResult(data) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/settings/PrivacyPolicyActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/settings/PrivacyPolicyActivity.kt new file mode 100644 index 0000000..383e373 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/settings/PrivacyPolicyActivity.kt @@ -0,0 +1,29 @@ +package com.ux.video.file.filerecovery.settings + +import android.content.Context +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.ux.video.file.filerecovery.R +import com.ux.video.file.filerecovery.base.BaseActivity +import com.ux.video.file.filerecovery.databinding.ActivityPrivacyPolicyBinding +import androidx.core.net.toUri + +class PrivacyPolicyActivity : BaseActivity() { + override fun inflateBinding(inflater: LayoutInflater): ActivityPrivacyPolicyBinding = + ActivityPrivacyPolicyBinding.inflate(layoutInflater) + + + override fun initView() { + super.initView() + + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/settings/SetupActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/settings/SetupActivity.kt new file mode 100644 index 0000000..ff66d55 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/settings/SetupActivity.kt @@ -0,0 +1,39 @@ +package com.ux.video.file.filerecovery.settings + +import android.content.Context +import android.view.LayoutInflater +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.net.toUri +import com.ux.video.file.filerecovery.R +import com.ux.video.file.filerecovery.base.BaseActivity +import com.ux.video.file.filerecovery.databinding.ActivitySetUpBinding +import com.ux.video.file.filerecovery.utils.Common + +class SetupActivity : BaseActivity() { + override fun inflateBinding(inflater: LayoutInflater): ActivitySetUpBinding = ActivitySetUpBinding.inflate(layoutInflater) + + override fun initView() { + super.initView() + } + + override fun initData() { + super.initData() + binding.run { + imageViewBack.setOnClickListener { finish() } + layoutShareApp.setOnClickListener { + Common.shareApp(this@SetupActivity) + } + layoutPrivacyPolicy.setOnClickListener { + openWebPage(this@SetupActivity,getString(R.string.privacy_policy_url )) + } + } + } + fun openWebPage(context: Context, url: String) { + val customTabsIntent = CustomTabsIntent.Builder() + .setShowTitle(true) + .build() + + customTabsIntent.launchUrl(context, url.toUri()) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoInfoActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoInfoActivity.kt deleted file mode 100644 index 8dc9506..0000000 --- a/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoInfoActivity.kt +++ /dev/null @@ -1,252 +0,0 @@ -package com.ux.video.file.filerecovery.sort - -import android.content.Intent -import android.graphics.drawable.Drawable -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 com.bumptech.glide.Glide -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.load.resource.bitmap.CenterCrop -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.RequestOptions -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 -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 -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video -import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx -import com.ux.video.file.filerecovery.video.PlayMediaManager -import com.ux.video.file.filerecovery.video.VideoPlayActivity -import java.io.File - -class PhotoInfoActivity : BaseActivity() { - - companion object { - val KEY_CLICK_ITEM = "click_item" - } - - private var scanType: Int = VALUE_SCAN_TYPE_photo - 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, ResultDataFiles::class.java) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(KEY_CLICK_ITEM) - } - scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) - setView() - - } - - - override fun initData() { - super.initData() - binding.run { - imageViewBack.setOnClickListener { finish() } - myData?.let { resultPhotosFiles -> - - tvName.text = resultPhotosFiles.name - tvPath.text = resultPhotosFiles.path - tvSize.text = resultPhotosFiles.sizeString - 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 { - RecoverOrDeleteManager.showConfirmDeleteDialog( - true, - supportFragmentManager, - lifecycleScope, - setOf(resultPhotosFiles) - ) { count -> - complete(count, 1) - } - } - } - layoutBottom.tvRight.run { - text = resources.getString(R.string.recover) - setOnClickListener { - RecoverOrDeleteManager.showRecoveringDialog( - supportFragmentManager, - lifecycleScope, - setOf(resultPhotosFiles) - ) { count -> - complete(count, 0) - } - } - } - } - } - } - - private fun setView() { - binding.run { - when (scanType) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { - layoutName.isVisible = true - layoutPath.isVisible = true - layoutResolution.isVisible = true - layoutDate.isVisible = true - frameImage.setBackgroundResource(0) - - - layoutSeekbar.isVisible = false - layoutType.isVisible = false - layoutSize.isVisible = false - layoutDuration.isVisible = false - - imPlay.isVisible = false - - myData?.targetFile?.let { loadImage(image,it) } - } - - VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> { - layoutName.isVisible = true - layoutPath.isVisible = true - layoutResolution.isVisible = true - layoutDate.isVisible = true - layoutDuration.isVisible = true - frameImage.setBackgroundResource(0) - - - layoutSeekbar.isVisible = false - layoutType.isVisible = false - layoutSize.isVisible = false - - imPlay.isVisible = true - myData?.let { data-> - data.targetFile?.let { loadImage(image,it) } - frameImage.setOnClickListener { - startActivity(Intent(this@PhotoInfoActivity, VideoPlayActivity::class.java).apply { - putExtra(VideoPlayActivity.KEY_DATA, data) - }) - } - } - } - - VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> { - Common.showLog("----------音频") - layoutName.isVisible = true - layoutPath.isVisible = true - layoutSize.isVisible = true - layoutDate.isVisible = true - layoutDuration.isVisible = true - layoutSeekbar.isVisible = true - imPlay.isVisible = true - frameImage.setBackgroundResource(R.drawable.bg_info_music_f2f2f7_8) - - loadCenterImage(image,R.drawable.image_info_music) - - initPlayAudio() - layoutResolution.isVisible = false - layoutType.isVisible = false - } - - VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { - layoutName.isVisible = true - layoutType.isVisible = true - layoutPath.isVisible = true - layoutSize.isVisible = true - layoutDate.isVisible = true - - 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) - .apply(RequestOptions().transform(CenterCrop(), RoundedCorners(8.dpToPx(this@PhotoInfoActivity)))) - .listener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target, - isFirstResource: Boolean - ): Boolean { - return false - } - - override fun onResourceReady( - resource: Drawable, - model: Any, - target: Target?, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - - return false - } - - }) - .into(image) - } - - - private fun initPlayAudio(){ - myData?.targetFile?.let { - binding.run { - PlayMediaManager(context = this@PhotoInfoActivity, mediaFile = it, - seekBar = seekBar, playBtn = imPlay, onUpdateProgress = { current,total-> - textTimeCurrent.text = current - textTimeTotal.text = total - } ) - } - } - } - - - private fun complete(number: Int, type: Int) { - finish() - startActivity(Intent(this@PhotoInfoActivity, RecoverySuccessActivity::class.java).apply { - putExtra(RecoverySuccessActivity.KEY_SUCCESS_COUNT, number) - putExtra(RecoverySuccessActivity.KEY_SUCCESS_TYPE, type) - }) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDialogFragment.kt index 72f5009..907e712 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDialogFragment.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDialogFragment.kt @@ -16,7 +16,7 @@ import com.ux.video.file.filerecovery.databinding.DialogSortBinding class SortDialogFragment(val onClickSort: (type: Int) -> Unit) : DialogFragment() { private lateinit var binding: DialogSortBinding - private var clickType = PhotoSortingActivity.SORT_DESC_DATE + private var clickType = SortingActivity.SORT_DESC_DATE private lateinit var LayoutList: List override fun onStart() { diff --git a/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDisplayAdapter.kt similarity index 85% rename from app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateAdapter.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/SortDisplayAdapter.kt index 15ca6ea..f40c9f2 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDisplayAdapter.kt @@ -10,22 +10,22 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.ux.video.file.filerecovery.base.BaseAdapter import com.ux.video.file.filerecovery.databinding.PhotoDisplayDateAdapterBinding import com.ux.video.file.filerecovery.db.ResultDataFiles -import com.ux.video.file.filerecovery.utils.Common -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents -import com.ux.video.file.filerecovery.utils.ScanRepository +import com.ux.video.file.filerecovery.db.isThumbnail -class PhotoDisplayDateAdapter( +import com.ux.video.file.filerecovery.utils.FileType + +/** + * 排序页面显示 + */ +class SortDisplayAdapter( mContext: Context, - var scanType: Int, + var fileType: FileType, var mColumns: Int, - var viewModel: ScanRepository, + var viewModel: SortingViewModel, var onSelectedUpdate: (resultDataFiles: ResultDataFiles, isAdd: Boolean) -> Unit, var clickItem: (item: ResultDataFiles) -> Unit ) : BaseAdapter>, PhotoDisplayDateAdapterBinding>(mContext) { - - private var allSelected: Boolean? = null override fun getViewBinding(parent: ViewGroup): PhotoDisplayDateAdapterBinding = @@ -41,7 +41,7 @@ class PhotoDisplayDateAdapter( */ fun getTotalChildCount(hideThumbnails: Boolean): Int { if (hideThumbnails) { - return data.sumOf { it.second.filter { !it.isThumbnail }.size } + return data.sumOf { it.second.filter { !it.isThumbnail() }.size } } else { return data.sumOf { it.second.size } } @@ -80,9 +80,9 @@ class PhotoDisplayDateAdapter( holder.vb.run { item.run { val (date, files) = item - val childAdapter = PhotoDisplayDateChildAdapter( + val childAdapter = SortDisplayChildAdapter( mContext, - scanType, + fileType, mColumns, viewModel, { resultPhotosFiles, addOrRemove, isDateAllSelected -> @@ -108,8 +108,8 @@ class PhotoDisplayDateAdapter( textChildCounts.text = "(${files.size})" recyclerChild.apply { - layoutManager = when (scanType) { - Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { + layoutManager = when (fileType) { + FileType.AUDIO, FileType.DOCUMENT -> { LinearLayoutManager(context) } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateChildAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDisplayChildAdapter.kt similarity index 84% rename from app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateChildAdapter.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/SortDisplayChildAdapter.kt index 0ca2ebc..163d8f8 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoDisplayDateChildAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortDisplayChildAdapter.kt @@ -25,16 +25,22 @@ import com.ux.video.file.filerecovery.databinding.FileSpanCountTwoAdapterBinding 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.db.duration +import com.ux.video.file.filerecovery.db.targetFile import com.ux.video.file.filerecovery.recovery.ui.recoveryphoto.RecoveryPhotoViewModel 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 +import com.ux.video.file.filerecovery.utils.FileType import com.ux.video.file.filerecovery.utils.ScanManager -import com.ux.video.file.filerecovery.utils.ScanRepository -class PhotoDisplayDateChildAdapter( + +/** + * 排序页面显示 、已经恢复的文件页面显示 + */ +class SortDisplayChildAdapter( mContext: Context, - var scanType: Int, + var fileType: FileType, var mColumns: Int, var viewModel: ViewModel? = null, /** @@ -72,12 +78,12 @@ class PhotoDisplayDateChildAdapter( } override fun getItemViewType(position: Int): Int { - when (scanType) { - Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio -> { + when (fileType) { + FileType.AUDIO -> { return TYPE_AUDIO } - Common.VALUE_SCAN_TYPE_documents, Common.VALUE_SCAN_TYPE_deleted_documents -> { + FileType.DOCUMENT -> { return TYPE_DOCUMENTS } @@ -172,20 +178,10 @@ class PhotoDisplayDateChildAdapter( item.run { holder.vb.let { it.textName.text = name - it.textDuration.text = Common.formatDuration(duration) + it.textDuration.text = Common.formatDuration(duration()) it.textSize.text = sizeString 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) } @@ -200,7 +196,7 @@ class PhotoDisplayDateChildAdapter( it.textName.text = name it.textDate.text = Common.getItemMonthDay(lastModified) it.textSize.text = sizeString - targetFile?.let { file -> + targetFile().let { file -> it.imageIcon.setImageResource(Common.getFileIconRes(file)) } @@ -235,11 +231,11 @@ class PhotoDisplayDateChildAdapter( private fun initAudioDocuments(imageSelect: ImageView, item: ResultDataFiles) { viewModel?.let { - if (it is ScanRepository) { + if (it is SortingViewModel) { it.checkIsSelect(item) } else { it as RecoveryPhotoViewModel - it.checkIsSelect(item) + it.checkIsSelect(fileType, item) }.let { isSelected -> imageSelect.isSelected = isSelected == true addOrRemove(item, isSelected == true) @@ -265,31 +261,21 @@ class PhotoDisplayDateChildAdapter( ) { item.run { 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( - when (scanType) { - Common.VALUE_SCAN_TYPE_photo, Common.VALUE_SCAN_TYPE_deleted_photo -> R.drawable.icon_type_photo - Common.VALUE_SCAN_TYPE_video, Common.VALUE_SCAN_TYPE_deleted_video -> R.drawable.icon_type_video + when (fileType) { + FileType.PHOTO -> R.drawable.icon_type_photo + FileType.VIDEO -> R.drawable.icon_type_video else -> R.drawable.icon_type_photo } ) Glide.with(mContext) - .load(targetFile) + .load(targetFile()) .apply( RequestOptions() .transform(CenterCrop(), RoundedCorners(8.dpToPx(mContext))) ) + .error(R.drawable.photo_place_holder) .listener(object : RequestListener { override fun onLoadFailed( e: GlideException?, @@ -299,7 +285,7 @@ class PhotoDisplayDateChildAdapter( ): Boolean { ScanManager.showLog( "加载图片", - "-------path = ${path} file=${targetFile}" + "-------path = ${path} e=${e?.message}" ) return false } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoSortingActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortingActivity.kt similarity index 81% rename from app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoSortingActivity.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/SortingActivity.kt index 43c0539..c55c2d3 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/sort/PhotoSortingActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortingActivity.kt @@ -6,8 +6,8 @@ import android.text.TextWatcher import android.util.Log import android.view.LayoutInflater import android.widget.LinearLayout +import androidx.activity.viewModels import androidx.core.view.isVisible -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager @@ -15,17 +15,10 @@ 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.db.isThumbnail +import com.ux.video.file.filerecovery.detail.DetailsActivity 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 -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 -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video import com.ux.video.file.filerecovery.utils.Common.setItemSelect import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterByDuration @@ -36,19 +29,19 @@ import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterRemoveThumbnai import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterThumbnailsAsync import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRange import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRangeList -import com.ux.video.file.filerecovery.utils.ExtendFunctions.getParcelableArrayListExtraCompat import com.ux.video.file.filerecovery.utils.ExtendFunctions.kbToBytes import com.ux.video.file.filerecovery.utils.ExtendFunctions.mbToBytes import com.ux.video.file.filerecovery.utils.ExtendFunctions.minutesToMillisecond import com.ux.video.file.filerecovery.utils.ExtendFunctions.removeItem +import com.ux.video.file.filerecovery.utils.FileType import com.ux.video.file.filerecovery.utils.GridSpacingItemDecoration -import com.ux.video.file.filerecovery.utils.ScanRepository +import com.ux.video.file.filerecovery.utils.ScanType import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.Date -class PhotoSortingActivity : BaseActivity() { +class SortingActivity : BaseActivity() { companion object { //指定文件夹下的所有文件 @@ -67,14 +60,14 @@ class PhotoSortingActivity : BaseActivity() { val SORT_DESC_DATE = 3 } - private var scanType: Int = VALUE_SCAN_TYPE_photo + private lateinit var fileType: FileType private var sortDialogFragment: SortDialogFragment? = null private var columns = 3 - private var dateAdapter: PhotoDisplayDateAdapter? = null + private var dateAdapter: SortDisplayAdapter? = null //文件大小排序使用的适配器 - private var sizeSortAdapter: PhotoDisplayDateChildAdapter? = null + private var sizeSortAdapter: SortDisplayChildAdapter? = null private var dialogCustomerDateStart: DatePickerDialogFragment? = null @@ -118,20 +111,86 @@ class PhotoSortingActivity : BaseActivity() { private lateinit var sizeFilterItemArray: Array + private val viewModel: SortingViewModel by viewModels() + private lateinit var list: List + + // private lateinit var viewModel: SortingViewModel + override fun inflateBinding(inflater: LayoutInflater): ActivityPhotoSortingBinding { + + return ActivityPhotoSortingBinding.inflate(inflater) + } - private lateinit var viewModel: ScanRepository - override fun inflateBinding(inflater: LayoutInflater): ActivityPhotoSortingBinding = - ActivityPhotoSortingBinding.inflate(inflater) override fun initData() { super.initData() - scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) - val list: ArrayList? = - intent.getParcelableArrayListExtraCompat(KEY_PHOTO_FOLDER_FILE) + val intExtra = intent.getIntExtra(Common.KEY_FILE_TYPE, FileType.PHOTO.value) + + fileType = FileType.from(intExtra)!! mItemDecoration = GridSpacingItemDecoration(columns, Common.itemSpacing, Common.horizontalSpacing) updateButtonCounts(0) - viewModel = ViewModelProvider(this)[ScanRepository::class.java] + viewModel.sortingData.observe(this) { sortData -> + list = sortData + + list.let { + binding.tvThumbnailCounts.text = + getString(R.string.hide_thumbnails, it.filter { it.isThumbnail() }.size) + + //降序(最近的在前面) + sortByDateReverse = Common.getSortByDayNewToOldInit(it) + //升序(时间最远的在前面) + sortedByDatePositive = Common.getSortByDayOldToNew(sortByDateReverse) + sortBySizeBigToSmall = Common.getSortBySizeBigToSmall(it) + sortBySizeSmallToBig = Common.getSortBySizeSmallToBig(it) + + sizeSortAdapter = SortDisplayChildAdapter( + this@SortingActivity, + fileType, + columns, viewModel, + { resultPhotosFiles, isAdd, allSelected -> + viewModel.toggleSelection(isAdd, resultPhotosFiles) + }, { hide -> + + + }) { item -> + startActivity( + Intent( + this@SortingActivity, + DetailsActivity::class.java + ).apply { + putExtra(Common.KEY_FILE_TYPE, item.fileType.value) + putExtra(DetailsActivity.KEY_CLICK_ITEM, item) + }) + + } + dateAdapter = + SortDisplayAdapter( + this@SortingActivity, + fileType, + columns, + viewModel, + { actionPath, isAdd -> + viewModel.toggleSelection(isAdd, actionPath) + }) { item -> + startActivity( + Intent( + this@SortingActivity, + DetailsActivity::class.java + ).apply { + putExtra(Common.KEY_FILE_TYPE, fileType.value) + putExtra(DetailsActivity.KEY_CLICK_ITEM, item) + }) + }.apply { + setData(sortByDateReverse) + resetCurrentDateList(sortByDateReverse) + } + setDateAdapter() + setSingleDelete() + setFilter() + setAllClick() + + } + } viewModel.selectedLiveData.observe(this) { selectedSet -> allSelectedSetList = selectedSet @@ -144,64 +203,7 @@ class PhotoSortingActivity : BaseActivity() { updateCurrentIsAllSelectStatus() } setScanTypeView() - list?.let { - binding.tvThumbnailCounts.text = - getString(R.string.hide_thumbnails, it.filter { it.isThumbnail }.size) - //降序(最近的在前面) - sortByDateReverse = Common.getSortByDayNewToOldInit(it) - //升序(时间最远的在前面) - sortedByDatePositive = Common.getSortByDayOldToNew(sortByDateReverse) - sortBySizeBigToSmall = Common.getSortBySizeBigToSmall(it) - sortBySizeSmallToBig = Common.getSortBySizeSmallToBig(it) - - sizeSortAdapter = PhotoDisplayDateChildAdapter( - this@PhotoSortingActivity, - scanType, - columns, viewModel, - { resultPhotosFiles, isAdd, allSelected -> - viewModel.toggleSelection(isAdd, resultPhotosFiles) - }, { hide -> - - - }) { item -> - startActivity( - Intent( - this@PhotoSortingActivity, - PhotoInfoActivity::class.java - ).apply { - putExtra(KEY_SCAN_TYPE, scanType) - putExtra(PhotoInfoActivity.KEY_CLICK_ITEM, item) - }) - - } - dateAdapter = - PhotoDisplayDateAdapter( - this@PhotoSortingActivity, - scanType, - columns, - viewModel, - { actionPath, isAdd -> - viewModel.toggleSelection(isAdd, actionPath) - }) { item -> - startActivity( - Intent( - this@PhotoSortingActivity, - PhotoInfoActivity::class.java - ).apply { - putExtra(KEY_SCAN_TYPE, scanType) - putExtra(PhotoInfoActivity.KEY_CLICK_ITEM, item) - }) - }.apply { - setData(sortByDateReverse) - resetCurrentDateList(sortByDateReverse) - } - setDateAdapter() - setSingleDelete() - setFilter() - setAllClick() - - } } @@ -210,7 +212,7 @@ class PhotoSortingActivity : BaseActivity() { imageViewBack.setOnClickListener { finish() } switchHideThumbnails.setOnCheckedChangeListener { _, isChecked -> when (recyclerView.adapter) { - is PhotoDisplayDateAdapter -> { + is SortDisplayAdapter -> { lifecycleScope.launch { dateAdapter?.run { initGetCurrentDateList().let { list -> @@ -225,7 +227,7 @@ class PhotoSortingActivity : BaseActivity() { } - is PhotoDisplayDateChildAdapter -> { + is SortDisplayChildAdapter -> { lifecycleScope.launch { sizeSortAdapter?.run { initGetCurrentSizeList().let { @@ -336,7 +338,7 @@ class PhotoSortingActivity : BaseActivity() { tvSelectAll.setOnClickListener { it.isSelected = !it.isSelected when (binding.recyclerView.adapter) { - is PhotoDisplayDateAdapter -> { + is SortDisplayAdapter -> { dateAdapter?.setAllSelected(it.isSelected) dateAdapter?.getCurrentData()?.let { it as List>> @@ -346,7 +348,7 @@ class PhotoSortingActivity : BaseActivity() { } - is PhotoDisplayDateChildAdapter -> { + is SortDisplayChildAdapter -> { sizeSortAdapter?.setAllSelected(it.isSelected) sizeSortAdapter?.getCurrentData()?.let { it as List @@ -367,43 +369,47 @@ class PhotoSortingActivity : BaseActivity() { */ private fun setScanTypeView() { binding.run { - when (scanType) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { - titleSize.text = getString(R.string.size) - filterLayoutLinearlayout.isVisible = true - relativeThumbnails.isVisible = true - layoutSearch.isVisible = false - sizeFilterItemArray = resources.getStringArray(R.array.filter_size_photo) - } + fileType?.let { + when (it) { + FileType.PHOTO -> { + titleSize.text = getString(R.string.size) + filterLayoutLinearlayout.isVisible = true + relativeThumbnails.isVisible = true + layoutSearch.isVisible = false + sizeFilterItemArray = resources.getStringArray(R.array.filter_size_photo) + } - VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> { - titleSize.text = getString(R.string.duration) - filterLayoutLinearlayout.isVisible = true - relativeThumbnails.isVisible = false - layoutSearch.isVisible = false - sizeFilterItemArray = - resources.getStringArray(R.array.filter_duration_video_audio) - } + FileType.VIDEO -> { + titleSize.text = getString(R.string.duration) + filterLayoutLinearlayout.isVisible = true + relativeThumbnails.isVisible = false + layoutSearch.isVisible = false + sizeFilterItemArray = + resources.getStringArray(R.array.filter_duration_video_audio) + } - VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> { - titleSize.text = getString(R.string.duration) - filterLayoutLinearlayout.isVisible = false - relativeThumbnails.isVisible = false - layoutSearch.isVisible = true - sizeFilterItemArray = - resources.getStringArray(R.array.filter_duration_video_audio) - initSearch() - } + FileType.AUDIO -> { + titleSize.text = getString(R.string.duration) + filterLayoutLinearlayout.isVisible = false + relativeThumbnails.isVisible = false + layoutSearch.isVisible = true + sizeFilterItemArray = + resources.getStringArray(R.array.filter_duration_video_audio) + initSearch() + } - VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { - titleSize.text = getString(R.string.size) - filterLayoutLinearlayout.isVisible = false - relativeThumbnails.isVisible = false - layoutSearch.isVisible = true - sizeFilterItemArray = resources.getStringArray(R.array.filter_documents_size) - initSearch() - } - } + FileType.DOCUMENT -> { + titleSize.text = getString(R.string.size) + filterLayoutLinearlayout.isVisible = false + relativeThumbnails.isVisible = false + layoutSearch.isVisible = true + sizeFilterItemArray = resources.getStringArray(R.array.filter_documents_size) + initSearch() + } + + + } + } } } @@ -413,8 +419,8 @@ class PhotoSortingActivity : BaseActivity() { binding.tvSelectCounts.text = it.toString() updateButtonCounts(it) when (binding.recyclerView.adapter) { - is PhotoDisplayDateAdapter -> { - val adapter = binding.recyclerView.adapter as PhotoDisplayDateAdapter + is SortDisplayAdapter -> { + val adapter = binding.recyclerView.adapter as SortDisplayAdapter if (it > 0) { binding.tvSelectAll.isSelected = it == adapter.getTotalChildCount(false) } else { @@ -423,8 +429,8 @@ class PhotoSortingActivity : BaseActivity() { } - is PhotoDisplayDateChildAdapter -> { - val adapter = binding.recyclerView.adapter as PhotoDisplayDateChildAdapter + is SortDisplayChildAdapter -> { + val adapter = binding.recyclerView.adapter as SortDisplayChildAdapter if (it > 0) { binding.tvSelectAll.isSelected = it == adapter.itemCount @@ -448,7 +454,7 @@ class PhotoSortingActivity : BaseActivity() { currentDateList = null binding.tvThumbnailCounts.text = - getString(R.string.hide_thumbnails, currentList.filter { it.isThumbnail }.size) + getString(R.string.hide_thumbnails, currentList.filter { it.isThumbnail() }.size) } @@ -461,7 +467,7 @@ class PhotoSortingActivity : BaseActivity() { currentDateList = currentList currentSizeList = null val totalSelectedCount = currentList.sumOf { pair -> - pair.second.filter { it.isThumbnail }.size + pair.second.filter { it.isThumbnail() }.size } binding.tvThumbnailCounts.text = getString(R.string.hide_thumbnails, totalSelectedCount) @@ -474,7 +480,7 @@ class PhotoSortingActivity : BaseActivity() { private fun setDateAdapter() { binding.recyclerView.run { adapter = dateAdapter?.apply { setColumns(columns) } - layoutManager = LinearLayoutManager(this@PhotoSortingActivity) + layoutManager = LinearLayoutManager(this@SortingActivity) setPadding(0, 0, 0, 70.dpToPx(context)) clipToPadding = false } @@ -484,8 +490,8 @@ class PhotoSortingActivity : BaseActivity() { binding.recyclerView.run { val aPx = 16.dpToPx(context) val bottom = 70.dpToPx(context) - when (scanType) { - VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio,VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { + when (fileType) { + FileType.AUDIO, FileType.DOCUMENT-> { layoutManager = LinearLayoutManager(context) setPadding(aPx, 0, 0, bottom) } @@ -513,7 +519,7 @@ class PhotoSortingActivity : BaseActivity() { setItemSelect(it as LinearLayout, true) resources.getStringArray(R.array.filter_date).let { data -> filterDatePopupWindows = filterDatePopupWindows ?: DateFilterPopupWindows( - this@PhotoSortingActivity, + this@SortingActivity, 0, { clickValue, showDialog -> when (clickValue) { @@ -576,7 +582,7 @@ class PhotoSortingActivity : BaseActivity() { setItemSelect(it as LinearLayout, true) sizeFilterItemArray.let { data -> filterSizePopupWindows = filterSizePopupWindows ?: FilterPopupWindows( - this@PhotoSortingActivity, + this@SortingActivity, data, 0, { clickValue -> @@ -598,7 +604,7 @@ class PhotoSortingActivity : BaseActivity() { setItemSelect(it as LinearLayout, true) resources.getStringArray(R.array.filter_layout).let { data -> filterLayoutPopupWindows = filterLayoutPopupWindows ?: FilterPopupWindows( - this@PhotoSortingActivity, + this@SortingActivity, data, 1, { clickValue -> @@ -608,13 +614,13 @@ class PhotoSortingActivity : BaseActivity() { data[2] -> columns = 4 } when (binding.recyclerView.adapter) { - is PhotoDisplayDateAdapter -> { + is SortDisplayAdapter -> { dateAdapter?.setColumns(columns) } - is PhotoDisplayDateChildAdapter -> { + is SortDisplayChildAdapter -> { binding.recyclerView.layoutManager = - GridLayoutManager(this@PhotoSortingActivity, columns) + GridLayoutManager(this@SortingActivity, columns) sizeSortAdapter?.setColumns(columns) } } @@ -654,10 +660,10 @@ class PhotoSortingActivity : BaseActivity() { private fun startFilter() { Common.showLog("--------------开始筛选") - val filterSizeCovert = filterSizeCovert(scanType, filterSize) + val filterSizeCovert = filterSizeCovert(fileType, filterSize) when (binding.recyclerView.adapter) { //当前是时间排序 - is PhotoDisplayDateAdapter -> { + is SortDisplayAdapter -> { //确定当前排序 val list = if (sortReverse) sortByDateReverse else sortedByDatePositive list.filterWithinDateRange( @@ -665,8 +671,8 @@ class PhotoSortingActivity : BaseActivity() { startDate = if (filterDate == FILTER_DATE_CUSTOMER) filterStartDate else null, endDate = if (filterDate == FILTER_DATE_CUSTOMER) filterEndDate else null ).run { - when (scanType) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { + when (fileType) { + FileType.PHOTO,FileType.DOCUMENT -> { filterBySize(filterSizeCovert.first, filterSizeCovert.second) } @@ -684,15 +690,15 @@ class PhotoSortingActivity : BaseActivity() { } //当前是大小排序 - is PhotoDisplayDateChildAdapter -> { + is SortDisplayChildAdapter -> { val list = if (sortReverse) sortBySizeBigToSmall else sortBySizeSmallToBig list.filterWithinDateRangeList( filterDate, startDate = if (filterDate == FILTER_DATE_CUSTOMER) filterStartDate else null, endDate = if (filterDate == FILTER_DATE_CUSTOMER) filterEndDate else null ).run { - when (scanType) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { + when (fileType) { + FileType.PHOTO,FileType.DOCUMENT-> { filterBySizeList(filterSizeCovert.first, filterSizeCovert.second) } @@ -736,9 +742,9 @@ class PhotoSortingActivity : BaseActivity() { } - private fun filterSizeCovert(scanType: Int, filterSize: String): Pair { - when (scanType) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { + private fun filterSizeCovert(fileType: FileType, filterSize: String): Pair { + when (fileType) { + FileType.PHOTO -> { val stringArray = resources.getStringArray(R.array.filter_size_photo) return when (filterSize) { stringArray[0] -> Pair(-1L, -1L) @@ -749,7 +755,7 @@ class PhotoSortingActivity : BaseActivity() { } } - VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video, VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> { + FileType.VIDEO, FileType.AUDIO -> { val stringArray = resources.getStringArray(R.array.filter_duration_video_audio) return when (filterSize) { stringArray[0] -> Pair(-1L, -1L) @@ -761,7 +767,7 @@ class PhotoSortingActivity : BaseActivity() { } } - VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { + FileType.DOCUMENT-> { val stringArray = resources.getStringArray(R.array.filter_documents_size) return when (filterSize) { stringArray[0] -> Pair(-1L, -1L) @@ -773,7 +779,6 @@ class PhotoSortingActivity : BaseActivity() { } } - return Pair(-1L, -1L) } /** @@ -846,9 +851,10 @@ class PhotoSortingActivity : BaseActivity() { * @param type 0 恢复 1 删除 */ private fun complete(number: Int, type: Int) { - startActivity(Intent(this@PhotoSortingActivity, RecoverySuccessActivity::class.java).apply { + startActivity(Intent(this@SortingActivity, RecoverySuccessActivity::class.java).apply { putExtra(RecoverySuccessActivity.KEY_SUCCESS_COUNT, number) putExtra(RecoverySuccessActivity.KEY_SUCCESS_TYPE, type) + putExtra(Common.KEY_FILE_TYPE, fileType.value) }) if (type == 1) { lifecycleScope.launch { @@ -1019,11 +1025,11 @@ class PhotoSortingActivity : BaseActivity() { private fun checkTypeAfter(onAction: (isDateSort: Boolean) -> Unit) { when (binding.recyclerView.adapter) { - is PhotoDisplayDateAdapter -> { + is SortDisplayAdapter -> { onAction(true) } - is PhotoDisplayDateChildAdapter -> { + is SortDisplayChildAdapter -> { onAction(false) } } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortingViewModel.kt similarity index 88% rename from app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt rename to app/src/main/java/com/ux/video/file/filerecovery/sort/SortingViewModel.kt index 67ede9b..7f23a1f 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/sort/SortingViewModel.kt @@ -1,12 +1,14 @@ -package com.ux.video.file.filerecovery.utils +package com.ux.video.file.filerecovery.sort import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.ux.video.file.filerecovery.db.ResultData import com.ux.video.file.filerecovery.db.ResultDataFiles +import com.ux.video.file.filerecovery.result.ScanResultRepository +import com.ux.video.file.filerecovery.utils.Common - -class ScanRepository : ViewModel() { +class SortingViewModel : ViewModel() { // private val _selectedFlow = MutableStateFlow>(mutableSetOf()) // val selectedFlow: StateFlow> = _selectedFlow // @@ -14,7 +16,7 @@ class ScanRepository : ViewModel() { // private val _selectedDisplayFlow = MutableStateFlow>(mutableSetOf()) // val selectedDisplayFlow: StateFlow> = _selectedDisplayFlow - + val sortingData: MutableLiveData> = ScanResultRepository.sortingData private val _selectedLiveData = MutableLiveData>(emptySet()) val selectedLiveData: LiveData> = _selectedLiveData @@ -71,7 +73,7 @@ class ScanRepository : ViewModel() { /** * 详情页删除完毕,移除删除掉的数据(单个删除) */ - fun afterSingleDeleted(deletedItem:ResultDataFiles){ + fun afterSingleDeleted(deletedItem: ResultDataFiles){ val selected = _selectedLiveData.value.orEmpty().toMutableSet() val display = _selectedDisplayLiveData.value.orEmpty().toMutableSet() @@ -90,4 +92,4 @@ class ScanRepository : ViewModel() { } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/success/RecoverySuccessActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/success/RecoverySuccessActivity.kt index 94c0522..97c5ab7 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/success/RecoverySuccessActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/success/RecoverySuccessActivity.kt @@ -1,36 +1,15 @@ package com.ux.video.file.filerecovery.success import android.content.Intent -import android.os.Environment import android.view.LayoutInflater import androidx.core.view.isVisible -import androidx.lifecycle.lifecycleScope import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.databinding.ActivityRecoverOrDeletedSuccessBinding -import com.ux.video.file.filerecovery.databinding.ActivityScanningBinding import com.ux.video.file.filerecovery.main.MainActivity -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_AUDIO -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_DOCUMENT -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_PHOTO -import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_VIDEO -import com.ux.video.file.filerecovery.result.ScanResultDisplayActivity +import com.ux.video.file.filerecovery.recovery.RecoveryActivity import com.ux.video.file.filerecovery.utils.Common -import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE -import com.ux.video.file.filerecovery.utils.Common.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 -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video -import com.ux.video.file.filerecovery.utils.ScanManager -import com.ux.video.file.filerecovery.utils.ScanRepository -import com.ux.video.file.filerecovery.utils.ScanState -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.launch +import com.ux.video.file.filerecovery.utils.FileType class RecoverySuccessActivity : BaseActivity() { @@ -39,7 +18,7 @@ class RecoverySuccessActivity : BaseActivity tvFileType.text = + when (fileType) { + FileType.PHOTO -> tvFileType.text = resources.getString(R.string.describe_photos) - VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> tvFileType.text = + FileType.VIDEO -> tvFileType.text = resources.getString(R.string.describe_videos) - VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> tvFileType.text = + FileType.AUDIO -> tvFileType.text = resources.getString(R.string.describe_audios) - VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> tvFileType.text = + FileType.DOCUMENT -> tvFileType.text = resources.getString(R.string.describe_documents) } when (successType) { @@ -86,7 +68,13 @@ class RecoverySuccessActivity : BaseActivity, keyword: String @@ -112,7 +105,6 @@ object Common { } - /** * 格式化文件大小显示 */ @@ -261,19 +253,7 @@ object Common { } - /** - * 去掉缩略图的集合 - */ - suspend fun filterThumbnailsAsync( - originalList: MutableList>> - ): List>> = withContext(Dispatchers.Default) { - originalList.asSequence() - .map { (key, files) -> - key to files.asSequence().filter { !it.isThumbnail }.toList() - } - .filter { it.second.isNotEmpty() } - .toList() - } + fun removeSelectedFromList( @@ -294,12 +274,12 @@ object Common { } - fun getMediaDuration(filePath: String): Long { val retriever = MediaMetadataRetriever() return try { retriever.setDataSource(filePath) - val durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) + val durationStr = + retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) durationStr?.toLongOrNull() ?: 0L // 单位:毫秒 } catch (e: Exception) { e.printStackTrace() @@ -323,6 +303,7 @@ object Common { String.format("%02d:%02d", minutes, seconds) } } + fun getFileMIME(file: File): String { val ext = file.extension.lowercase() val mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext) @@ -340,6 +321,7 @@ object Common { mime == "application/zip" || mime == "application/x-rar-compressed" || mime == "application/x-7z-compressed" -> "archive" + else -> "other" } } @@ -348,15 +330,17 @@ object Common { "xapk" to "application/zip", // 可以继续添加其他自定义扩展名 ) + fun getMimeTypeParts(file: File): String { val extension = file.extension.lowercase() - val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)?:customMimeMap[extension] + 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" + "$mainType/$subType" } else { "unknown" } @@ -380,7 +364,8 @@ object Common { fun getFormatDate(time: Long): String { return dateFormat.format(Date(time)) } - fun getItemMonthDay(time: Long): String{ + + fun getItemMonthDay(time: Long): String { return itemDateFormat.format(time) } @@ -400,4 +385,117 @@ object Common { fun showLog(msg: String) { Log.d("============", msg) } + + + fun shareSingleFile(context: Context, file: File, fileType: FileType) { + val uri = FileProvider.getUriForFile( + context, + "${context.packageName}.fileprovider", // 与 Manifest 保持一致 + file + ) + + val intent = Intent(Intent.ACTION_SEND).apply { + type = getMediaFileStr(fileType) + putExtra(Intent.EXTRA_STREAM, uri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + context.startActivity(Intent.createChooser(intent, "分享文件")) + } + + fun shareMultipleFiles(context: Context, files: List, fileType: FileType) { + val uris = files.map { + FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", it) + } + + val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply { + type = getMediaFileStr(fileType) + putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris)) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + context.startActivity(Intent.createChooser(intent, "分享多个文件")) + } + + fun getResolution(type: FileType, file: File): String { + return when (type) { + FileType.PHOTO -> { + getImageSize(file).run { + "$first*$second" + } + } + + FileType.VIDEO -> getVideoResolution(file.path).run { + "$first*$second" + } + + else -> "" + } + } + + private fun getMediaFileStr(fileType: FileType): String { + return when (fileType) { + FileType.PHOTO -> "image/*" + FileType.VIDEO -> "video/*" + FileType.AUDIO -> "audio/*" + FileType.DOCUMENT -> "application/*" + } + } + + private fun getImageSize(file: File): Pair { + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + BitmapFactory.decodeFile(file.absolutePath, options) + val width = options.outWidth + val height = options.outHeight + return Pair(width, height) + } + + fun shareApp(context: Context) { + val appPackageName = context.packageName + val shareIntent = Intent(Intent.ACTION_SEND).apply { + type = "text/plain" + putExtra( + Intent.EXTRA_TEXT, + "I recommend a useful app:\nhttps://play.google.com/store/apps/details?id=$appPackageName" + ) + } + context.startActivity( + Intent.createChooser(shareIntent, "分享应用") + ) + } + + fun getScanType(scanType: ScanType) { + when (scanType) { + ScanType.ALL_PHOTO -> {} + ScanType.DELETED_PHOTO -> {} + ScanType.ALL_VIDEO -> {} + ScanType.DELETED_VIDEO -> {} + ScanType.ALL_AUDIO -> {} + ScanType.DELETED_AUDIO -> {} + ScanType.ALL_DOCUMENT -> {} + ScanType.DELETED_DOCUMENT -> {} + + } + + } + fun switchFileType(fileType: FileType,onPhoto:()-> Unit,onVideo:()-> Unit,onAudio:()-> Unit,onDocument:()-> Unit){ + when (fileType) { + FileType.PHOTO -> {onPhoto()} + FileType.VIDEO -> {onVideo()} + FileType.AUDIO -> {onAudio()} + FileType.DOCUMENT -> {onDocument()} + } + } + + fun getScanType1(scanType: ScanType) { + when (scanType) { + ScanType.ALL_PHOTO, ScanType.DELETED_PHOTO -> {} + ScanType.ALL_VIDEO, ScanType.DELETED_VIDEO -> {} + ScanType.ALL_AUDIO, ScanType.DELETED_AUDIO -> {} + ScanType.ALL_DOCUMENT, ScanType.DELETED_DOCUMENT -> {} + } + + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt index 1a16505..03fee9b 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt @@ -8,6 +8,8 @@ import android.os.Parcelable import android.util.TypedValue import androidx.recyclerview.widget.RecyclerView import com.ux.video.file.filerecovery.db.ResultDataFiles +import com.ux.video.file.filerecovery.db.duration +import com.ux.video.file.filerecovery.db.isThumbnail import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.util.Date @@ -24,6 +26,8 @@ object ExtendFunctions { } inline fun Intent.getParcelableArrayListExtraCompat(key: String): ArrayList? { + extras?.classLoader = T::class.java.classLoader + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { getParcelableArrayListExtra(key, T::class.java) } else { @@ -134,7 +138,7 @@ object ExtendFunctions { maxSize: Long ): List { if (minSize == -1L) return this - return this.filter { it.duration in minSize..maxSize } + return this.filter { it.duration() in minSize..maxSize } } /** @@ -160,7 +164,7 @@ object ExtendFunctions { ): List>> { if (minSize == -1L) return this return this.mapNotNull { (date, files) -> - val filtered = files.filter { it.duration in minSize..maxSize } + val filtered = files.filter { it.duration() in minSize..maxSize } if (filtered.isNotEmpty()) date to filtered else null } } @@ -185,7 +189,7 @@ object ExtendFunctions { withContext(Dispatchers.Default) { this@filterThumbnailsAsync.asSequence() .mapNotNull { (key, files) -> - val filtered = files.asSequence().filter { !it.isThumbnail }.toList() + val filtered = files.asSequence().filter { !it.isThumbnail() }.toList() if (filtered.isNotEmpty()) key to filtered else null } .toList() @@ -198,7 +202,7 @@ object ExtendFunctions { suspend fun List.filterRemoveThumbnailsAsync(): List = withContext(Dispatchers.Default) { this@filterRemoveThumbnailsAsync.asSequence() - .filter { !it.isThumbnail } // 去掉 isThumbnail = true 的项 + .filter { !it.isThumbnail() } // 去掉 isThumbnail = true 的项 .toList() } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/FileType.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/FileType.kt new file mode 100644 index 0000000..b2ecada --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/FileType.kt @@ -0,0 +1,13 @@ +package com.ux.video.file.filerecovery.utils + +enum class FileType(val value: Int,val tabIndex: Int) { + PHOTO(8,0), + VIDEO(9,1), + AUDIO(10,2), + DOCUMENT(11,3); + + companion object { + fun from(value: Int): FileType? = + FileType.entries.find { it.value == value } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt index 85dbd0c..8e210e4 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt @@ -1,23 +1,17 @@ package com.ux.video.file.filerecovery.utils +import android.content.ContentUris import android.content.Context import android.graphics.BitmapFactory import android.media.MediaMetadataRetriever import android.net.Uri +import android.provider.MediaStore import android.provider.OpenableColumns import android.text.format.Formatter import android.util.Log 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 -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo -import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.currentCoroutineContext @@ -56,7 +50,7 @@ object ScanManager { context: Context, root: File, maxDepth: Int = 5, maxFiles: Int = 5000, - type: Int + fileType: FileType ): Flow = flow { val result = mutableMapOf>() @@ -71,24 +65,7 @@ object ScanManager { if (file.isDirectory) { scanDocuments(file, depth + 1) } else { - var fileCheckBoolean: Boolean = false - when (type) { - VALUE_SCAN_TYPE_photo -> { - fileCheckBoolean = isFormatFile(file, IMAGE_FILE) && isValidImage(file) - } - - VALUE_SCAN_TYPE_video -> { - fileCheckBoolean = isFormatFile(file, VIDEO_FILE) - } - - VALUE_SCAN_TYPE_audio -> { - fileCheckBoolean = isFormatFile(file, AUDIO_FILE) - } - - VALUE_SCAN_TYPE_documents -> { - fileCheckBoolean = file.isFile && isFormatFile(file, DOCUMENT_FILE) - } - } + val fileCheckBoolean: Boolean = checkFileFormat(file,fileType) if (fileCheckBoolean) { val dirName = file.parentFile?.name ?: "Unknown" @@ -114,8 +91,7 @@ object ScanManager { file.length() ), lastModified = file.lastModified(), - resolution = getResolution(type, file), - fileType = getFileType(type) + fileType = fileType ) } ResultData(dir, ArrayList(resultDataFilesList)) @@ -159,9 +135,9 @@ object ScanManager { fun scanHiddenPhotoAsync( context: Context, root: File, maxDepth: Int = 5, - maxFiles: Int = 5000, type: Int + maxFiles: Int = 5000, fileType: FileType ): Flow = flow { - +// scanRecycler(context) val result = mutableMapOf>() var fileCount = 0 @@ -169,46 +145,52 @@ object ScanManager { suspend fun scanDir(dir: File, depth: Int, insideHidden: Boolean = false) { if (!dir.exists() || !dir.isDirectory) return if (depth > maxDepth || fileCount >= maxFiles) return + val skipDirs = setOf( + "Android", + "obb", + "data", + ".thumbnails", + ".cache" + ) + + if (skipDirs.contains(dir.name)) return showLog("HiddenScan", "${dir.name} 111111") + dir.listFiles()?.forEach { file -> if (file.isDirectory) { val isHidden = file.name.startsWith(".") scanDir(file, depth + 1, insideHidden = insideHidden || isHidden) } else { + val fileCheckBoolean: Boolean = checkFileFormat(file,fileType) if (insideHidden) { - var fileCheckBoolean: Boolean = false - when (type) { - VALUE_SCAN_TYPE_deleted_photo -> { - fileCheckBoolean = - isFormatFile(file, IMAGE_FILE) && isValidImage(file) - } - - VALUE_SCAN_TYPE_deleted_video -> { - fileCheckBoolean = isFormatFile(file, VIDEO_FILE) - } - - VALUE_SCAN_TYPE_deleted_audio -> { - fileCheckBoolean = isFormatFile(file, AUDIO_FILE) - } - - VALUE_SCAN_TYPE_deleted_documents -> { - fileCheckBoolean = file.isFile && isFormatFile(file, DOCUMENT_FILE) - } - } if (fileCheckBoolean) { val dirName = file.parentFile?.name ?: "Unknown" - ScanManager.showLog("HiddenScan", "${dirName} 22222") + ScanManager.showLog("HiddenScan", "${dirName} 22222 ${file.name}") val list = result.getOrPut(dirName) { mutableListOf() } list.add(file) fileCount++ emit(ScanState.Progress(fileCount, file.absolutePath)) } + } else { + if (file.name.startsWith(".") && fileCheckBoolean) { + val dirName = file.parentFile?.name ?: "Unknown" + ScanManager.showLog("HiddenScan", "${dirName} 33333 ${file.name}") + val list = result.getOrPut(dirName) { mutableListOf() } + list.add(file) + fileCount++ + emit(ScanState.Progress(fileCount, file.absolutePath)) + } + } } } } + + + + scanDir(root, depth = 0) - ScanManager.showLog("HiddenScan", " 3333") + ScanManager.showLog("HiddenScan", " 44444444") val map = result.map { (dir, files) -> val resultDataFilesList = files.map { file -> ResultDataFiles( @@ -220,8 +202,7 @@ object ScanManager { file.length() ), lastModified = file.lastModified(), - resolution = getResolution(type, file), - fileType = getFileType(type) + fileType = fileType ) } @@ -231,18 +212,64 @@ object ScanManager { } - private fun getFileType(scanType: Int): Int { - return when (scanType) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> 0 + private fun checkFileFormat(file: File, fileType: FileType): Boolean { + return when (fileType) { + FileType.PHOTO -> { + isFormatFile(file, IMAGE_FILE) + } - VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> 1 + FileType.VIDEO -> { + isFormatFile(file, VIDEO_FILE) + } - VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> 2 - VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> 3 - else -> 3 + FileType.AUDIO -> { + isFormatFile(file, AUDIO_FILE) + } + + FileType.DOCUMENT -> { + file.isFile && isFormatFile(file, DOCUMENT_FILE) + } } } + private fun scanRecycler(context: Context) { + val projection = arrayOf( + MediaStore.Images.Media._ID, + MediaStore.Images.Media.DISPLAY_NAME + ) + + val selection = "${MediaStore.Images.Media.IS_TRASHED} = ?" + val selectionArgs = arrayOf("1") + + val cursor = context.contentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + projection, + selection, + selectionArgs, + null + ) + Log.d("Trash", "cursor=$cursor ") + cursor?.use { + Log.d("Trash", "11111111") + while (it.moveToNext()) { + Log.d("Trash", "2222222222") + val id = it.getLong(0) + val name = it.getString(1) + + val uri = ContentUris.withAppendedId( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + id + ) + + // 这是“可恢复”的文件 + Log.d("Trash", "name=$name uri=$uri") + } + } + + } + + + private fun getFileSizeByMediaStore(context: Context, file: File): Long { val uri = Uri.fromFile(file) context.contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null) @@ -255,23 +282,24 @@ object ScanManager { return file.length() // fallback } - private fun getResolution(type: Int, file: File): String { - return when (type) { - VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { - getImageSize(file).run { - "$first*$second" - } - } - - VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> getVideoResolution(file.path).run { - "$first*$second" - } - - else -> "" - } - } +// private fun getResolution(type: Int, file: File): String { +// return when (type) { +// VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { +// getImageSize(file).run { +// "$first*$second" +// } +// } +// +// VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> getVideoResolution(file.path).run { +// "$first*$second" +// } +// +// else -> "" +// } +// } private fun isFormatFile(file: File, types: List): Boolean { + if (!file.exists() || file.length() <= 0) return false val ext = file.extension.lowercase() return types.contains(ext) } @@ -284,6 +312,7 @@ object ScanManager { Log.d(tag, msg) } + fun isValidImage(file: File): Boolean { if (!file.exists() || file.length() <= 0) return false return try { diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanType.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanType.kt new file mode 100644 index 0000000..d1fee9e --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanType.kt @@ -0,0 +1,38 @@ +package com.ux.video.file.filerecovery.utils + +enum class ScanType(val value: Int) { + + ALL_PHOTO(0), + DELETED_PHOTO(1), + + ALL_VIDEO(2), + DELETED_VIDEO(3), + + ALL_AUDIO(4), + DELETED_AUDIO(5), + + ALL_DOCUMENT(6), + DELETED_DOCUMENT(7); + + + val mediaType: FileType + get() = when (this) { + ALL_PHOTO, DELETED_PHOTO -> FileType.PHOTO + ALL_VIDEO, DELETED_VIDEO -> FileType.VIDEO + ALL_AUDIO, DELETED_AUDIO -> FileType.AUDIO + ALL_DOCUMENT, DELETED_DOCUMENT -> FileType.DOCUMENT + } + + val isDeletedScan: Boolean + get() = when (this) { + DELETED_PHOTO, + DELETED_VIDEO, + DELETED_AUDIO, + DELETED_DOCUMENT -> true + else -> false + } + companion object { + fun from(value: Int): ScanType? = + ScanType.entries.find { it.value == value } + } +} diff --git a/app/src/main/java/com/ux/video/file/filerecovery/video/PlayMediaManager.kt b/app/src/main/java/com/ux/video/file/filerecovery/video/PlayMediaManager.kt index f6283df..02cdbc3 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/video/PlayMediaManager.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/video/PlayMediaManager.kt @@ -107,4 +107,9 @@ class PlayMediaManager(var context: Context, var mediaFile: File, var playView: } }) } + + fun stopPlayAudio(){ + player.stop() + + } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/video/VideoPlayActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/video/VideoPlayActivity.kt index c79ec6a..8c0b4fc 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/video/VideoPlayActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/video/VideoPlayActivity.kt @@ -15,8 +15,10 @@ import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.databinding.ActivityVideoPlayBinding import com.ux.video.file.filerecovery.sort.RecoverOrDeleteManager import com.ux.video.file.filerecovery.db.ResultDataFiles +import com.ux.video.file.filerecovery.db.targetFile import com.ux.video.file.filerecovery.success.RecoverySuccessActivity import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.FileType class VideoPlayActivity : BaseActivity() { @@ -48,10 +50,10 @@ class VideoPlayActivity : BaseActivity() { binding.run { myData?.let { resultPhotosFiles-> imageBack.setOnClickListener { finish() } - resultPhotosFiles.targetFile?.let { + resultPhotosFiles.targetFile().let { PlayMediaManager(context = this@VideoPlayActivity, mediaFile = it, playView = playerView, seekBar = seekBar, playBtn = playImage, onUpdateProgress = { current,total-> - textTimeCurrent.text = current + textTimeCurrent.text = current textTimeTotal.text = total } ) } @@ -141,7 +143,7 @@ class VideoPlayActivity : BaseActivity() { myData?.let { player = ExoPlayer.Builder(this).build() binding.playerView.player = player - val mediaItem = MediaItem.fromUri(Uri.fromFile(it.targetFile)) + val mediaItem = MediaItem.fromUri(Uri.fromFile(it.targetFile())) player.addListener(object : Player.Listener { override fun onPlaybackStateChanged(playbackState: Int) { super.onPlaybackStateChanged(playbackState) @@ -177,6 +179,7 @@ class VideoPlayActivity : BaseActivity() { startActivity(Intent(this@VideoPlayActivity, RecoverySuccessActivity::class.java).apply { putExtra(RecoverySuccessActivity.KEY_SUCCESS_COUNT, number) putExtra(RecoverySuccessActivity.KEY_SUCCESS_TYPE, type) + putExtra(Common.KEY_FILE_TYPE, FileType.VIDEO.value) }) } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/welcome/SplashActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/welcome/SplashActivity.kt new file mode 100644 index 0000000..5c2c06a --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/welcome/SplashActivity.kt @@ -0,0 +1,28 @@ +package com.ux.video.file.filerecovery.welcome + +import android.content.Intent +import android.view.LayoutInflater +import com.ux.video.file.filerecovery.base.BaseActivity +import com.ux.video.file.filerecovery.databinding.ActivitySplashBinding +import com.ux.video.file.filerecovery.main.MainActivity +import com.ux.video.file.filerecovery.recovery.RecoveryActivity + +class SplashActivity : BaseActivity() { + + + override fun inflateBinding(inflater: LayoutInflater): ActivitySplashBinding = + ActivitySplashBinding.inflate(inflater) + + override fun initView() { + super.initView() + binding.textEnter.setOnClickListener { + startActivity(Intent(this@SplashActivity, MainActivity::class.java)) + finish() + } + } + override fun initData() { + super.initData() + } + + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_000000_10.xml b/app/src/main/res/drawable/bg_000000_10.xml new file mode 100644 index 0000000..ba7636c --- /dev/null +++ b/app/src/main/res/drawable/bg_000000_10.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_welcome_enter.xml b/app/src/main/res/drawable/bg_welcome_enter.xml new file mode 100644 index 0000000..1e16ff3 --- /dev/null +++ b/app/src/main/res/drawable/bg_welcome_enter.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_privacy_policy.png b/app/src/main/res/drawable/icon_privacy_policy.png new file mode 100644 index 0000000..9873b1b Binary files /dev/null and b/app/src/main/res/drawable/icon_privacy_policy.png differ diff --git a/app/src/main/res/drawable/icon_share_app.png b/app/src/main/res/drawable/icon_share_app.png new file mode 100644 index 0000000..9e41fee Binary files /dev/null and b/app/src/main/res/drawable/icon_share_app.png differ diff --git a/app/src/main/res/drawable/photo_place_holder.png b/app/src/main/res/drawable/photo_place_holder.png new file mode 100644 index 0000000..e46ad88 Binary files /dev/null and b/app/src/main/res/drawable/photo_place_holder.png differ diff --git a/app/src/main/res/drawable/welcome_bg.png b/app/src/main/res/drawable/welcome_bg.png new file mode 100644 index 0000000..be25ad5 Binary files /dev/null and b/app/src/main/res/drawable/welcome_bg.png differ diff --git a/app/src/main/res/layout/activity_photo_info.xml b/app/src/main/res/layout/activity_details.xml similarity index 99% rename from app/src/main/res/layout/activity_photo_info.xml rename to app/src/main/res/layout/activity_details.xml index 6ced089..6bb3daf 100644 --- a/app/src/main/res/layout/activity_photo_info.xml +++ b/app/src/main/res/layout/activity_details.xml @@ -7,7 +7,7 @@ android:layout_height="match_parent" android:background="@color/white" android:orientation="vertical" - tools:context=".sort.PhotoInfoActivity"> + tools:context=".detail.DetailsActivity"> @@ -186,7 +187,7 @@ android:id="@+id/layout_recovery" android:layout_width="0dp" android:layout_height="60dp" - android:layout_marginStart="16dp" + android:layout_marginHorizontal="16dp" android:layout_marginTop="16dp" android:background="@drawable/main_type_bg" android:gravity="center_vertical" @@ -219,6 +220,7 @@ android:layout_height="60dp" android:layout_marginStart="11dp" android:layout_marginEnd="16dp" + android:visibility="gone" android:background="@drawable/main_type_bg" android:gravity="center_vertical" android:orientation="horizontal" @@ -243,66 +245,8 @@ app:fontType="bold" /> - -