This commit is contained in:
litingting 2025-12-26 13:58:57 +08:00
parent b069402b22
commit ca7a7f9da6
68 changed files with 2192 additions and 1314 deletions

BIN
app/FileRecovery Normal file

Binary file not shown.

View File

@ -1,11 +1,16 @@
import java.text.SimpleDateFormat
import java.util.Date
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
id ("kotlin-kapt") id ("kotlin-kapt")
id ("kotlin-parcelize") id ("kotlin-parcelize")
id("io.objectbox") id("io.objectbox")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
} }
val timestamp = SimpleDateFormat("MM_dd_HH_mm").format(Date())
android { android {
namespace = "com.ux.video.file.filerecovery" namespace = "com.ux.video.file.filerecovery"
compileSdk = 36 compileSdk = 36
@ -16,13 +21,13 @@ android {
targetSdk = 36 targetSdk = 36
versionCode = 1 versionCode = 1
versionName = "1.0" versionName = "1.0"
project.setProperty("archivesBaseName", "File Recovery Tool" + versionName + "(${versionCode})_$timestamp")
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
release { release {
isMinifyEnabled = false isMinifyEnabled = true
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
@ -56,9 +61,17 @@ dependencies {
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)
implementation (libs.glide) implementation (libs.glide)
kapt (libs.compiler) 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 ("com.google.android.material:material:1.13.0")
implementation(project(":pickerview")) implementation(project(":pickerview"))
implementation ("androidx.media3:media3-exoplayer:1.8.0") implementation ("androidx.media3:media3-exoplayer:1.8.0")
implementation ("androidx.media3:media3-ui: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")
} }

29
app/google-services.json Normal file
View File

@ -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"
}

View File

@ -43,11 +43,6 @@
"id": "7:8305655849151310709", "id": "7:8305655849151310709",
"name": "lastModified", "name": "lastModified",
"type": 6 "type": 6
},
{
"id": "8:5134665607056785092",
"name": "resolution",
"type": 9
} }
], ],
"relations": [] "relations": []
@ -71,7 +66,8 @@
3831579248666795267, 3831579248666795267,
4445203567182319472, 4445203567182319472,
6858261029979256233, 6858261029979256233,
2835789057594891780 2835789057594891780,
5134665607056785092
], ],
"retiredRelationUids": [], "retiredRelationUids": [],
"version": 1 "version": 1

View File

@ -19,3 +19,62 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-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 {
<fields>;
<methods>;
}
-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 {
<fields>;
<methods>;
}
-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 { *; }

View File

@ -13,49 +13,76 @@
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/recovery"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/recovery"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.FileRecovery" android:theme="@style/Theme.FileRecovery"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".recovery.RecoveryActivity" android:name=".settings.PrivacyPolicyActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".video.VideoPlayActivity" android:name=".settings.SetupActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".sort.PhotoInfoActivity" android:name=".welcome.SplashActivity"
android:exported="false" /> android:exported="true"
<activity android:screenOrientation="portrait"
android:name=".sort.PhotoSortingActivity" tools:ignore="SplashScreen">
android:exported="false" />
<activity
android:name=".result.ScanResultDisplayActivity"
android:exported="false" />
<activity
android:name=".result.ScanningActivity"
android:exported="false" />
<activity
android:name=".main.ScanSelectTypeActivity"
android:exported="false" />
<activity
android:name=".documents.DocumentsScanResultActivity"
android:exported="false" />
<activity
android:name=".success.RecoverySuccessActivity"
android:exported="false" />
<activity
android:name=".main.MainActivity"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".recovery.RecoveryActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".video.VideoPlayActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".detail.DetailsActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".sort.SortingActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".result.ScanResultDisplayActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".result.ScanningActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".main.ScanSelectTypeActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".success.RecoverySuccessActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".main.MainActivity"
android:exported="false"
android:screenOrientation="portrait" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application> </application>
</manifest> </manifest>

View File

@ -13,6 +13,9 @@ import androidx.fragment.app.DialogFragment
import com.ux.video.file.filerecovery.databinding.DialogRecoveringBinding import com.ux.video.file.filerecovery.databinding.DialogRecoveringBinding
/**
* 删除/恢复中父类弹窗
*/
abstract class BaseIngDialogFragment() : DialogFragment() { abstract class BaseIngDialogFragment() : DialogFragment() {
var total: Int = 0 var total: Int = 0
var completeListener: ((number: Int) -> Unit)? = null var completeListener: ((number: Int) -> Unit)? = null

View File

@ -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<FileType, Int> {
override fun convertToDatabaseValue(entityProperty: FileType?): Int? {
return entityProperty?.value
}
override fun convertToEntityProperty(databaseValue: Int?): FileType? {
return FileType.entries.find { it.value == databaseValue }
}
}

View File

@ -1,13 +1,17 @@
package com.ux.video.file.filerecovery.db package com.ux.video.file.filerecovery.db
import android.content.Context import android.content.Context
import androidx.lifecycle.LifecycleCoroutineScope
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.FileType
import io.objectbox.Box import io.objectbox.Box
import io.objectbox.BoxStore import io.objectbox.BoxStore
import io.objectbox.config.DebugFlags import io.objectbox.config.DebugFlags
import io.objectbox.reactive.DataSubscription import io.objectbox.reactive.DataSubscription
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlin.jvm.java import kotlin.jvm.java
@ -92,6 +96,21 @@ object ObjectBoxManager {
} }
} }
fun deleteRecoveryFilesAsyncById(lifecycleCoroutineScope: LifecycleCoroutineScope,list: List<ResultDataFiles>, 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 { // fun queryIsLike(id: Long): Boolean {
// val likeBox: Box<LikeWallpaper>? = getLikeBox() // val likeBox: Box<LikeWallpaper>? = getLikeBox()
@ -113,10 +132,10 @@ object ObjectBoxManager {
return list return list
} }
suspend fun queryRecoveryFileAsync(type: Int): List<ResultDataFiles> = suspend fun queryRecoveryFileAsync(type: FileType): List<ResultDataFiles> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val dataBox = getDataBox() val dataBox = getDataBox()
dataBox.query(ResultDataFiles_.fileType.equal(type)) dataBox.query(ResultDataFiles_.fileType.equal(type.value))
.build() .build()
.find() .find()
} }

View File

@ -4,6 +4,8 @@ import java.io.File
import android.os.Parcelable import android.os.Parcelable
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.FileType
import io.objectbox.annotation.Convert
import io.objectbox.annotation.Entity import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id import io.objectbox.annotation.Id
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -13,35 +15,40 @@ import kotlinx.parcelize.Parcelize
data class ResultDataFiles( data class ResultDataFiles(
@Id @Id
var id: Long = 0, 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 name: String,
val path: String, val path: String,
val size: Long, // 字节 val size: Long, // 字节
val sizeString: String, val sizeString: String,
val lastModified: Long, // 时间戳 val lastModified: Long, // 时间戳
var resolution: String // 尺寸
) : Parcelable { ) : Parcelable {
val targetFile: File? // val targetFile: File
get() = path?.let { File(it) } // get() = path.let { File(it) }
//尺寸
// val resolution: String
// get() = Common.getResolution(fileType, targetFile)
//是否为缩略图文件(宽高任一小于 256 //是否为缩略图文件(宽高任一小于 256
val isThumbnail: Boolean // val isThumbnail: Boolean
get() { // get() {
val parts = resolution.lowercase().split("*").mapNotNull { // val parts = resolution.lowercase().split("*").mapNotNull {
it.trim().toIntOrNull() // it.trim().toIntOrNull()
} // }
if (parts.size == 2) { // if (parts.size == 2) {
val (width, height) = parts // val (width, height) = parts
return width < 256 || height < 256 // return width < 256 || height < 256
} // }
return false // return false
} // }
//音视频时长 //音视频时长
val duration: Long // val duration: Long
get() { // get() {
return Common.getMediaDuration(path.toString()) } // return Common.getMediaDuration(path.toString())
// }
} }

View File

@ -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)

View File

@ -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<ActivityDetailsBinding>() {
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<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable?>,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable?>?,
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()
}
}

View File

@ -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<ActivityDocumentsScanResultBinding>() {
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)
// }
}
}

View File

@ -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<ResultData, DocumentsScanResultAdapterBinding>(mContext) {
override fun getViewBinding(parent: ViewGroup): DocumentsScanResultAdapterBinding =
DocumentsScanResultAdapterBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
override fun bindItem(
holder: VHolder<DocumentsScanResultAdapterBinding>,
item: ResultData
) {
holder.vb.run {
item.run {
tvDirName.text = dirName
tvFileCount.text = allFiles.size.toString()
}
}
}
}

View File

@ -10,25 +10,25 @@ import android.os.Build
import android.os.Environment import android.os.Environment
import android.provider.Settings import android.provider.Settings
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.isVisible
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityMainBinding import com.ux.video.file.filerecovery.databinding.ActivityMainBinding
import com.ux.video.file.filerecovery.recovery.RecoveryActivity 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 import com.ux.video.file.filerecovery.utils.ScanManager
class MainActivity : BaseActivity<ActivityMainBinding>() { class MainActivity : BaseActivity<ActivityMainBinding>() {
private var dialogCustomerDateStart:DatePickerDialogFragment? = null private var pendingAction: NavigateAction? = null
private var dialogPermission: PermissionDialogFragment? = null private var dialogPermission: PermissionDialogFragment? = null
//是否正确引导用户打开所有文件管理权限 //是否正确引导用户打开所有文件管理权限
private var isRequestPermission = false private var isRequestPermission = false
private var currentGoType = ScanSelectTypeActivity.Companion.VALUE_PHOTO
private val requestPermissionLauncher = private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
var isAllOK = true var isAllOK = true
@ -54,47 +54,49 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun initView() { override fun initView() {
super.initView() super.initView()
onBackPressedDispatcher.addCallback( // onBackPressedDispatcher.addCallback(
this, // this,
object : OnBackPressedCallback(true) { // object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() { // override fun handleOnBackPressed() {
ScanManager.showLog("权限", "====0000000") // ScanManager.showLog("权限", "====0000000")
if (binding.layoutPermission.isVisible) { // if (binding.layoutPermission.isVisible) {
ScanManager.showLog("权限", "====111111") // ScanManager.showLog("权限", "====111111")
binding.layoutPermission.visibility = View.GONE //// binding.layoutPermission.visibility = View.GONE
} else { // } else {
ScanManager.showLog("权限", "====222222222222") // ScanManager.showLog("权限", "====222222222222")
finish() // finish()
} // }
} // }
}) // })
} }
override fun initData() { override fun initData() {
super.initData() super.initData()
initTitleColor() initTitleColor()
binding.run { binding.run {
allow.setOnClickListener { imSetting.setOnClickListener {
startActivity(Intent(this@MainActivity, SetupActivity::class.java))
} }
layoutPhoto.setOnClickListener { layoutPhoto.setOnClickListener {
currentGoType = ScanSelectTypeActivity.Companion.VALUE_PHOTO pendingAction = NavigateAction.Scan(FileType.PHOTO)
intentCheck() intentCheck()
} }
layoutVideo.setOnClickListener { layoutVideo.setOnClickListener {
currentGoType = ScanSelectTypeActivity.Companion.VALUE_VIDEO pendingAction = NavigateAction.Scan(FileType.VIDEO)
intentCheck() intentCheck()
} }
layoutAudio.setOnClickListener { layoutAudio.setOnClickListener {
currentGoType = ScanSelectTypeActivity.Companion.VALUE_AUDIO pendingAction = NavigateAction.Scan(FileType.AUDIO)
intentCheck() intentCheck()
} }
layoutDocument.setOnClickListener { layoutDocument.setOnClickListener {
currentGoType = ScanSelectTypeActivity.Companion.VALUE_DOCUMENT pendingAction = NavigateAction.Scan(FileType.DOCUMENT)
intentCheck() intentCheck()
} }
layoutRecovery.setOnClickListener { layoutRecovery.setOnClickListener {
startActivity(Intent(this@MainActivity,RecoveryActivity::class.java)) pendingAction = NavigateAction.Recovered
intentCheck()
} }
} }
binding.tvTitle.setOnClickListener { binding.tvTitle.setOnClickListener {
@ -102,53 +104,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
showDatePicker() 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<ResultPhotos>()
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() { private fun initTitleColor() {
@ -158,7 +113,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
val width = paint.measureText(text.toString()) val width = paint.measureText(text.toString())
val shader = LinearGradient( val shader = LinearGradient(
0f, 0f, width, textSize, 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, null,
Shader.TileMode.CLAMP Shader.TileMode.CLAMP
) )
@ -177,7 +135,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
isRequestPermission = false isRequestPermission = false
ScanManager.showLog("--", "-------onResume") ScanManager.showLog("--", "-------onResume")
startScan() startScan()
binding.layoutPermission.visibility = View.GONE
} }
} }
@ -236,14 +193,31 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
} }
private fun startScan() { 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() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
binding.layoutPermission.isVisible = false // binding.layoutPermission.isVisible = false
} }

View File

@ -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()
}

View File

@ -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.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityScanSelectTypeBinding import com.ux.video.file.filerecovery.databinding.ActivityScanSelectTypeBinding
import com.ux.video.file.filerecovery.result.ScanningActivity 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
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio import com.ux.video.file.filerecovery.utils.FileType
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio import com.ux.video.file.filerecovery.utils.ScanType
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 kotlin.properties.Delegates import kotlin.properties.Delegates
@ -23,14 +17,6 @@ import kotlin.properties.Delegates
*/ */
class ScanSelectTypeActivity : BaseActivity<ActivityScanSelectTypeBinding>() { class ScanSelectTypeActivity : BaseActivity<ActivityScanSelectTypeBinding>() {
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<Int>() private var allType by Delegates.notNull<Int>()
private var deletedType by Delegates.notNull<Int>() private var deletedType by Delegates.notNull<Int>()
override fun inflateBinding(inflater: LayoutInflater): ActivityScanSelectTypeBinding = override fun inflateBinding(inflater: LayoutInflater): ActivityScanSelectTypeBinding =
@ -39,8 +25,10 @@ class ScanSelectTypeActivity : BaseActivity<ActivityScanSelectTypeBinding>() {
override fun initView() { override fun initView() {
super.initView() super.initView()
val type = intent.getIntExtra(KEY_FILE_TYPE, VALUE_PHOTO) val intExtra = intent.getIntExtra(Common.KEY_FILE_TYPE, FileType.PHOTO.value)
setSelectType(type) val fileType = FileType.from(intExtra)
fileType?.let { setSelectType(it) }
} }
@ -50,40 +38,40 @@ class ScanSelectTypeActivity : BaseActivity<ActivityScanSelectTypeBinding>() {
binding.scanAllFile.setOnClickListener { binding.scanAllFile.setOnClickListener {
startActivity(Intent(this@ScanSelectTypeActivity, ScanningActivity::class.java).apply { startActivity(Intent(this@ScanSelectTypeActivity, ScanningActivity::class.java).apply {
putExtra(KEY_SCAN_TYPE, allType) putExtra(Common.KEY_SCAN_TYPE, allType)
}) })
} }
binding.scanDeletedFile.setOnClickListener { binding.scanDeletedFile.setOnClickListener {
startActivity(Intent(this@ScanSelectTypeActivity, ScanningActivity::class.java).apply { 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) { when (fileType) {
VALUE_PHOTO -> { FileType.PHOTO -> {
allType = VALUE_SCAN_TYPE_photo allType = ScanType.ALL_PHOTO.value
deletedType = VALUE_SCAN_TYPE_deleted_photo deletedType = ScanType.DELETED_PHOTO.value
binding.title.text = getString(R.string.photo_title) binding.title.text = getString(R.string.photo_title)
} }
VALUE_VIDEO -> { FileType.VIDEO -> {
allType = VALUE_SCAN_TYPE_video allType = ScanType.ALL_VIDEO.value
deletedType = VALUE_SCAN_TYPE_deleted_video deletedType = ScanType.DELETED_VIDEO.value
binding.title.text = getString(R.string.video_title) binding.title.text = getString(R.string.video_title)
} }
VALUE_AUDIO -> { FileType.AUDIO -> {
allType = VALUE_SCAN_TYPE_audio allType = ScanType.ALL_AUDIO.value
deletedType = VALUE_SCAN_TYPE_deleted_audio deletedType = ScanType.DELETED_AUDIO.value
binding.title.text = getString(R.string.audio_title) binding.title.text = getString(R.string.audio_title)
} }
VALUE_DOCUMENT -> { FileType.DOCUMENT -> {
allType = VALUE_SCAN_TYPE_documents allType = ScanType.ALL_DOCUMENT.value
deletedType = VALUE_SCAN_TYPE_deleted_documents deletedType = ScanType.DELETED_DOCUMENT.value
binding.title.text = getString(R.string.document_title) binding.title.text = getString(R.string.document_title)
} }
} }

View File

@ -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.databinding.ActivityRecoveryBinding
import com.ux.video.file.filerecovery.db.ObjectBoxManager 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.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.recovery.ui.recoveryphoto.RecoveryPhotoViewModel
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.CustomTextView import com.ux.video.file.filerecovery.utils.CustomTextView
import com.ux.video.file.filerecovery.utils.FileType
import com.ux.video.file.filerecovery.utils.ScanManager
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class RecoveryActivity : BaseActivity<ActivityRecoveryBinding>() { class RecoveryActivity : BaseActivity<ActivityRecoveryBinding>() {
val sharedViewModel: RecoveryPhotoViewModel by viewModels() val sharedViewModel: RecoveryPhotoViewModel by viewModels()
private lateinit var fileType: FileType
override fun inflateBinding(inflater: LayoutInflater): ActivityRecoveryBinding = override fun inflateBinding(inflater: LayoutInflater): ActivityRecoveryBinding =
ActivityRecoveryBinding.inflate(inflater) ActivityRecoveryBinding.inflate(inflater)
override fun initView() { override fun initView() {
super.initView() super.initView()
val intExtra = intent.getIntExtra(Common.KEY_FILE_TYPE, FileType.PHOTO.value)
fileType = FileType.from(intExtra)!!
binding.run { binding.run {
val fragments = listOf( val fragments = listOf(
RecoveryPhotoFragment.newInstance(Common.FILE_TYPE_PHOTO), RecoveredFragment.newInstance(FileType.PHOTO.value),
RecoveryPhotoFragment.newInstance(Common.FILE_TYPE_VIDEO), RecoveredFragment.newInstance(FileType.VIDEO.value),
RecoveryPhotoFragment.newInstance(Common.FILE_TYPE_AUDIO), RecoveredFragment.newInstance(FileType.AUDIO.value),
RecoveryPhotoFragment.newInstance(Common.FILE_TYPE_DOCUMENTS) RecoveredFragment.newInstance(FileType.DOCUMENT.value)
) )
viewPage2.adapter = MyViewPage2Adapter(this@RecoveryActivity, fragments) viewPage2.adapter = MyViewPage2Adapter(this@RecoveryActivity, fragments)
TabLayoutMediator(tabLayout, viewPage2) { tab, position -> TabLayoutMediator(tabLayout, viewPage2) { tab, position ->
@ -74,6 +80,17 @@ class RecoveryActivity : BaseActivity<ActivityRecoveryBinding>() {
}) })
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<ActivityRecoveryBinding>() {
binding.imageBack.setOnClickListener { binding.imageBack.setOnClickListener {
finish() 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 { lifecycleScope.launch {
val recoveryFilePhoto = ObjectBoxManager.queryRecoveryFileAsync(Common.FILE_TYPE_PHOTO) FileType.entries.forEach{ fileType->
sharedViewModel.setPhotosRecoveredFiles(recoveryFilePhoto) val recoveryFilePhoto = ObjectBoxManager.queryRecoveryFileAsync(fileType)
val recoveryFileVideo = ObjectBoxManager.queryRecoveryFileAsync(Common.FILE_TYPE_VIDEO) sharedViewModel.setRecoveredFiles(fileType, recoveryFilePhoto)
sharedViewModel.setVideosRecoveredFiles(recoveryFileVideo) setTabCount(fileType, recoveryFilePhoto.size)
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<CustomTextView>(R.id.tab_item_count)?.text =
getString(R.string.text_counts, recoveryFilePhoto.size)
getTabAt(1)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text =
getString(R.string.text_counts, recoveryFileVideo.size)
getTabAt(2)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text =
getString(R.string.text_counts, recoveryFileAudio.size)
getTabAt(3)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text =
getString(R.string.text_counts, recoveryFileDocuments.size)
} }
} }
} }
private fun setTabCount(fileType: FileType, number: Int) {
binding.tabLayout.run {
getTabAt(fileType.tabIndex)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text =
getString(R.string.text_counts, number)
// when (fileType) {
// FileType.PHOTO -> getTabAt(0)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text =
// getString(R.string.text_counts, number)
//
// FileType.VIDEO -> getTabAt(1)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text =
// getString(R.string.text_counts, number)
//
// FileType.AUDIO -> getTabAt(2)?.customView?.findViewById<CustomTextView>(R.id.tab_item_count)?.text =
// getString(R.string.text_counts, number)
//
// FileType.DOCUMENT -> getTabAt(3)?.customView?.findViewById<CustomTextView>(
// R.id.tab_item_count
// )?.text =
// getString(R.string.text_counts, number)
// }
}
}
} }

View File

@ -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
}
}

View File

@ -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<FragmentRecoveryPhotoBinding>() {
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<ResultDataFiles>? = null
private val sharedViewModel: RecoveryPhotoViewModel by activityViewModels()
private lateinit var recoveredList: List<ResultDataFiles>
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<ResultDataFiles>) {
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<ResultDataFiles>, 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()
}
}
}

View File

@ -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<FragmentRecoveryPhotoBinding>() {
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<ResultDataFiles>? = 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<ResultDataFiles>) {
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<ResultDataFiles>, 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)
}
}
}

View File

@ -5,8 +5,15 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.ux.video.file.filerecovery.db.ResultDataFiles import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.FileType
/**
*
* RecoveryActivity RecoveredFragment 共用
*/
class RecoveryPhotoViewModel : ViewModel() { class RecoveryPhotoViewModel : ViewModel() {
//用于及时更新tab 显示恢复文件的数量
private val _photoFiles = MutableLiveData<List<ResultDataFiles>>(emptyList()) private val _photoFiles = MutableLiveData<List<ResultDataFiles>>(emptyList())
val photoFiles: LiveData<List<ResultDataFiles>> = _photoFiles val photoFiles: LiveData<List<ResultDataFiles>> = _photoFiles
@ -19,24 +26,34 @@ class RecoveryPhotoViewModel : ViewModel() {
private val _documentsFiles = MutableLiveData<List<ResultDataFiles>>(emptyList()) private val _documentsFiles = MutableLiveData<List<ResultDataFiles>>(emptyList())
val documentsFiles: LiveData<List<ResultDataFiles>> = _documentsFiles val documentsFiles: LiveData<List<ResultDataFiles>> = _documentsFiles
private val _selectedLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
val selectedLiveData: LiveData<Set<ResultDataFiles>> = _selectedLiveData
fun setPhotosRecoveredFiles(photos:List<ResultDataFiles>){ private val _selectedPhotoLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
_photoFiles.value = photos val selectedPhoto: LiveData<Set<ResultDataFiles>> = _selectedPhotoLiveData
}
fun setVideosRecoveredFiles(videos:List<ResultDataFiles>){ private val _selectedVideoLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
_videoFiles.value = videos val selectedVideo: LiveData<Set<ResultDataFiles>> = _selectedVideoLiveData
}
fun setAudiosRecoveredFiles(audios:List<ResultDataFiles>){ private val _selectedAudioLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
_audioFiles.value = audios val selectedAudio: LiveData<Set<ResultDataFiles>> = _selectedAudioLiveData
}
fun setDocumentsRecoveredFiles(documents:List<ResultDataFiles>){ private val _selectedDocumentsLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
_documentsFiles.value = documents val selectedDocuments: LiveData<Set<ResultDataFiles>> = _selectedDocumentsLiveData
fun setRecoveredFiles(fileType: FileType, data: List<ResultDataFiles>) {
when (fileType) {
FileType.PHOTO -> _photoFiles.value = data
FileType.VIDEO -> _videoFiles.value = data
FileType.AUDIO -> _audioFiles.value = data
FileType.DOCUMENT -> _documentsFiles.value = data
} }
fun toggleSelection(isAdd: Boolean, resultDataFiles: ResultDataFiles) { }
val current = _selectedLiveData.value?.toMutableSet() ?: mutableSetOf()
fun toggleSelection(fileType: FileType, isAdd: Boolean, resultDataFiles: ResultDataFiles) {
getSelectedList(fileType).let { selectList ->
val current = selectList.value?.toMutableSet() ?: mutableSetOf()
resultDataFiles.let { resultDataFiles.let {
if (isAdd) { if (isAdd) {
current.add(it) current.add(it)
@ -44,11 +61,46 @@ class RecoveryPhotoViewModel : ViewModel() {
current.remove(it) current.remove(it)
} }
} }
_selectedLiveData.value = current.toSet() selectList.value = current.toSet()
} }
fun checkIsSelect(resultDataFiles: ResultDataFiles): Boolean { }
val current = _selectedLiveData.value
return current?.contains(resultDataFiles) == true fun clearSelected(fileType: FileType) {
getSelectedList(fileType).let {
it.value = it.value?.toMutableSet()?.apply {
clear()
} ?: emptySet()
}
}
fun removeSelectedFromSizeList(
list: List<ResultDataFiles>,
selectedLiveData: Set<ResultDataFiles>
): List<ResultDataFiles> {
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<Set<ResultDataFiles>> {
return when (fileType) {
FileType.PHOTO -> _selectedPhotoLiveData
FileType.VIDEO -> _selectedVideoLiveData
FileType.AUDIO -> _selectedAudioLiveData
FileType.DOCUMENT -> _selectedDocumentsLiveData
}
} }
} }

View File

@ -3,48 +3,37 @@ package com.ux.video.file.filerecovery.result
import android.content.Intent import android.content.Intent
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityScanResultDisplayBinding import com.ux.video.file.filerecovery.databinding.ActivityScanResultDisplayBinding
import com.ux.video.file.filerecovery.sort.PhotoSortingActivity
import com.ux.video.file.filerecovery.db.ResultData import com.ux.video.file.filerecovery.db.ResultData
import com.ux.video.file.filerecovery.db.ResultDataFiles 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.sort.SortingActivity
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio import com.ux.video.file.filerecovery.utils.FileType
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents import com.ux.video.file.filerecovery.utils.ScanType
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
/** /**
* 扫描结果汇总展示 * 扫描结果汇总展示
*/ */
class ScanResultDisplayActivity : BaseActivity<ActivityScanResultDisplayBinding>() { class ScanResultDisplayActivity : BaseActivity<ActivityScanResultDisplayBinding>() {
private var scanType: Int = VALUE_SCAN_TYPE_photo private lateinit var scanType: ScanType
private var exitDialog: ExitDialogFragment? = null private var exitDialog: ExitDialogFragment? = null
private var list: ArrayList<ResultData>? = null private lateinit var list: List<ResultData>
private val viewModel: ScanningResultDisplayViewModel by viewModels()
companion object {
val KEY_SCAN_RESULT = "scan_result"
}
override fun inflateBinding(inflater: LayoutInflater): ActivityScanResultDisplayBinding = override fun inflateBinding(inflater: LayoutInflater): ActivityScanResultDisplayBinding =
ActivityScanResultDisplayBinding.inflate(inflater) ActivityScanResultDisplayBinding.inflate(inflater)
override fun initView() { override fun initView() {
super.initView() super.initView()
list = intent.getParcelableArrayListExtraCompat(KEY_SCAN_RESULT) val intExtra = intent.getIntExtra(Common.KEY_SCAN_TYPE, ScanType.ALL_PHOTO.value)
scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) scanType = ScanType.from(intExtra)!!
setSelectTypeTitle(scanType) setSelectTypeTitle(scanType.mediaType)
} }
override fun initData() { override fun initData() {
@ -59,12 +48,12 @@ class ScanResultDisplayActivity : BaseActivity<ActivityScanResultDisplayBinding>
}) })
binding.run { binding.run {
val myAdapter = when (scanType) { val myAdapter = when (scanType.mediaType) {
VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { FileType.AUDIO, FileType.DOCUMENT -> {
bottomLayout.setBackgroundResource(R.drawable.bg_rectangle_white_top_20) bottomLayout.setBackgroundResource(R.drawable.bg_rectangle_white_top_20)
ScanResultDocumentsAdapter( ScanResultDocumentsAudioAdapter(
this@ScanResultDisplayActivity, this@ScanResultDisplayActivity,
scanType scanType.mediaType
) { folderLists -> ) { folderLists ->
goSort(folderLists) goSort(folderLists)
} }
@ -72,60 +61,56 @@ class ScanResultDisplayActivity : BaseActivity<ActivityScanResultDisplayBinding>
else -> { else -> {
bottomLayout.setBackgroundResource(0) bottomLayout.setBackgroundResource(0)
ScanResultPhotoAdapter( ScanResultPhotoVideoAdapter(
this@ScanResultDisplayActivity, this@ScanResultDisplayActivity,
scanType scanType.mediaType
) { folderLists -> ) { folderLists ->
goSort(folderLists) goSort(folderLists)
} }
} }
}
viewModel.scanData.observe(this@ScanResultDisplayActivity){ data->
}.apply { list = data
list?.let { list.let { data ->
textDirCount.text = it.size.toString() textDirCount.text = data.size.toString()
val sumOf = it.sumOf { it.allFiles.size } val sumOf = data.sumOf { it.allFiles.size }
textAllCounts.text = sumOf.toString() textAllCounts.text = sumOf.toString()
setData(it) myAdapter.setData(data)
} }
} }
recyclerResult.run { recyclerResult.run {
adapter = myAdapter adapter = myAdapter
layoutManager = LinearLayoutManager(this@ScanResultDisplayActivity) layoutManager = LinearLayoutManager(this@ScanResultDisplayActivity)
} }
} }
} }
private fun dealExit() { private fun dealExit() {
exitDialog = exitDialog ?: ExitDialogFragment() { exitDialog = exitDialog ?: ExitDialogFragment {
finish() finish()
} }
exitDialog?.show(supportFragmentManager, "") exitDialog?.show(supportFragmentManager, "")
} }
private fun setSelectTypeTitle(fileType: Int) { private fun setSelectTypeTitle(fileType: FileType) {
binding.run { binding.run {
when (fileType) { when (fileType) {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { FileType.PHOTO -> {
title.text = getString(R.string.photo_title) title.text = getString(R.string.photo_title)
textFileType.text = getString(R.string.text_photos) 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) title.text = getString(R.string.video_title)
textFileType.text = getString(R.string.text_videos) 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) title.text = getString(R.string.audio_title)
textFileType.text = getString(R.string.text_audios) 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) title.text = getString(R.string.document_title)
textFileType.text = getString(R.string.text_documents) textFileType.text = getString(R.string.text_documents)
} }
@ -137,16 +122,13 @@ class ScanResultDisplayActivity : BaseActivity<ActivityScanResultDisplayBinding>
private fun goSort(list: ArrayList<ResultDataFiles>) { private fun goSort(list: ArrayList<ResultDataFiles>) {
viewModel.updateSortData(list)
startActivity( startActivity(
Intent( Intent(
this@ScanResultDisplayActivity, this@ScanResultDisplayActivity,
PhotoSortingActivity::class.java SortingActivity::class.java
).apply { ).apply {
putExtra(KEY_SCAN_TYPE, scanType) putExtra(Common.KEY_FILE_TYPE, scanType.mediaType.value)
putParcelableArrayListExtra(
PhotoSortingActivity.KEY_PHOTO_FOLDER_FILE,
list
)
}) })
} }
} }

View File

@ -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.ResultData
import com.ux.video.file.filerecovery.db.ResultDataFiles import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.FileType
/** /**
* 文件或者音频的扫描结果汇总适配器 * 文件或者音频的扫描结果汇总适配器
*/ */
class ScanResultDocumentsAdapter( class ScanResultDocumentsAudioAdapter(
mContext: Context, mContext: Context,
var type: Int, var fileType: FileType,
var onClickItem: (allFiles: ArrayList<ResultDataFiles>) -> Unit var onClickItem: (allFiles: ArrayList<ResultDataFiles>) -> Unit
) : ) :
BaseAdapter<ResultData, ScanResultDocumentsAdapterBinding>(mContext) { BaseAdapter<ResultData, ScanResultDocumentsAdapterBinding>(mContext) {
@ -38,13 +39,15 @@ class ScanResultDocumentsAdapter(
relativeLayout.setOnClickListener { onClickItem(allFiles) } relativeLayout.setOnClickListener { onClickItem(allFiles) }
textDirName.text = dirName textDirName.text = dirName
textFileCounts.text = allFiles.size.toString() textFileCounts.text = allFiles.size.toString()
when(type){ when(fileType){
Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio->{ FileType.AUDIO ->{
icon.setImageResource(R.drawable.icon_folder_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) icon.setImageResource(R.drawable.icon_folder_documents)
} }
else -> Unit
} }
} }

View File

@ -2,6 +2,7 @@ package com.ux.video.file.filerecovery.result
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView 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.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseAdapter import com.ux.video.file.filerecovery.base.BaseAdapter
import com.ux.video.file.filerecovery.databinding.ScanResultAdapterBinding import com.ux.video.file.filerecovery.databinding.ScanResultAdapterBinding
import com.ux.video.file.filerecovery.db.ResultData import com.ux.video.file.filerecovery.db.ResultData
import com.ux.video.file.filerecovery.db.ResultDataFiles 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.ExtendFunctions.dpToPx
import com.ux.video.file.filerecovery.utils.FileType
import com.ux.video.file.filerecovery.utils.ScanManager
import java.io.File import java.io.File
class ScanResultPhotoAdapter( /**
* 图片或者视频的扫描结果汇总适配器
*/
class ScanResultPhotoVideoAdapter(
mContext: Context, mContext: Context,
var type: Int, var fileType: FileType,
var onClickItem: (allFiles: ArrayList<ResultDataFiles>) -> Unit var onClickItem: (allFiles: ArrayList<ResultDataFiles>) -> Unit
) : ) :
BaseAdapter<ResultData, ScanResultAdapterBinding>(mContext) { BaseAdapter<ResultData, ScanResultAdapterBinding>(mContext) {
@ -45,7 +53,7 @@ class ScanResultPhotoAdapter(
val takeFiles = allFiles.take(3) val takeFiles = allFiles.take(3)
imageViews.forEachIndexed { index, imageView -> imageViews.forEachIndexed { index, imageView ->
if (index < takeFiles.size) { if (index < takeFiles.size) {
takeFiles[index].targetFile?.let { takeFiles[index].targetFile().let {
loadImageView(mContext, it, imageView) loadImageView(mContext, it, imageView)
} }
} else { } else {
@ -61,12 +69,17 @@ class ScanResultPhotoAdapter(
} }
private fun loadImageView(context: Context, file: File, imageView: ImageView) { private fun loadImageView(context: Context, file: File, imageView: ImageView) {
ScanManager.showLog(
"加载图片",
"-----loadImageView--path = ${file.path}"
)
Glide.with(context) Glide.with(context)
.load(file) .load(file)
.apply( .apply(
RequestOptions() RequestOptions()
.transform(CenterCrop(), RoundedCorners(8.dpToPx(context))) .transform(CenterCrop(), RoundedCorners(8.dpToPx(context)))
) )
.error(R.drawable.photo_place_holder)
.into(imageView) .into(imageView)
} }
} }

View File

@ -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<List<ResultData>>()
//扫描用于排序显示的数据
val sortingData = MutableLiveData<List<ResultDataFiles>>()
fun setScanResult(data: List<ResultData>) {
scanData.postValue(data)
}
fun setSortResult(data: List<ResultDataFiles>) {
sortingData.postValue(data)
}
}

View File

@ -4,103 +4,83 @@ import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Environment import android.os.Environment
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.activity.viewModels
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityScanningBinding 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.utils.Common
import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_DOCUMENT import com.ux.video.file.filerecovery.utils.FileType
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.ScanManager 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.ScanState
import com.ux.video.file.filerecovery.utils.ScanType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import androidx.lifecycle.repeatOnLifecycle
class ScanningActivity : BaseActivity<ActivityScanningBinding>() { class ScanningActivity : BaseActivity<ActivityScanningBinding>() {
private val viewModel: ScanningActivityViewModel by viewModels()
private var scanType: Int = VALUE_SCAN_TYPE_photo private lateinit var scanType: ScanType
override fun inflateBinding(inflater: LayoutInflater): ActivityScanningBinding = override fun inflateBinding(inflater: LayoutInflater): ActivityScanningBinding =
ActivityScanningBinding.inflate(inflater) ActivityScanningBinding.inflate(inflater)
override fun initData() { override fun initData() {
super.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) setSelectType(scanType)
when (scanType) { if (scanType.isDeletedScan) {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_documents -> scanAll() scanDeleted()
VALUE_SCAN_TYPE_deleted_photo, VALUE_SCAN_TYPE_deleted_video, VALUE_SCAN_TYPE_deleted_audio, VALUE_SCAN_TYPE_deleted_documents -> scanDeleted() } else {
scanAll()
} }
binding.imageViewBack.setOnClickListener { finish() } binding.imageViewBack.setOnClickListener { finish() }
} }
private fun setSelectType(fileType: Int) { private fun setSelectType(scanType: ScanType) {
binding.run { binding.run {
when (scanType.mediaType) {
when (fileType) { FileType.PHOTO->{
VALUE_SCAN_TYPE_photo -> {
title.text = getString(R.string.photo_title) title.text = getString(R.string.photo_title)
tvScanDescribe.text = getString(R.string.describe_photos)
scanProgress.setCenterImage(R.drawable.im_photo_center_image) scanProgress.setCenterImage(R.drawable.im_photo_center_image)
} if(scanType.isDeletedScan){
VALUE_SCAN_TYPE_deleted_photo -> {
title.text = getString(R.string.photo_title)
tvScanDescribe.text = getString(R.string.describe_delete_photos) tvScanDescribe.text = getString(R.string.describe_delete_photos)
scanProgress.setCenterImage(R.drawable.im_photo_center_image) }else{
tvScanDescribe.text = getString(R.string.describe_photos)
} }
}
VALUE_SCAN_TYPE_video -> { FileType.VIDEO->{
title.text = getString(R.string.video_title) title.text = getString(R.string.video_title)
tvScanDescribe.text = getString(R.string.describe_videos)
scanProgress.setCenterImage(R.drawable.im_video_center_image) scanProgress.setCenterImage(R.drawable.im_video_center_image)
} if(scanType.isDeletedScan){
VALUE_SCAN_TYPE_deleted_video -> {
title.text = getString(R.string.video_title)
tvScanDescribe.text = getString(R.string.describe_delete_videos) tvScanDescribe.text = getString(R.string.describe_delete_videos)
scanProgress.setCenterImage(R.drawable.im_video_center_image) }else{
tvScanDescribe.text = getString(R.string.describe_videos)
}
} }
VALUE_SCAN_TYPE_audio -> { FileType.AUDIO->{
title.text = getString(R.string.audio_title) title.text = getString(R.string.audio_title)
tvScanDescribe.text = getString(R.string.describe_audios)
scanProgress.setCenterImage(R.drawable.im_audio_center_image) scanProgress.setCenterImage(R.drawable.im_audio_center_image)
} if(scanType.isDeletedScan){
VALUE_SCAN_TYPE_deleted_audio -> {
title.text = getString(R.string.audio_title)
tvScanDescribe.text = getString(R.string.describe_delete_audios) tvScanDescribe.text = getString(R.string.describe_delete_audios)
scanProgress.setCenterImage(R.drawable.im_audio_center_image) }else{
tvScanDescribe.text = getString(R.string.describe_audios)
} }
}
VALUE_SCAN_TYPE_documents -> { FileType.DOCUMENT->{
title.text = getString(R.string.document_title) title.text = getString(R.string.document_title)
tvScanDescribe.text = getString(R.string.describe_documents)
scanProgress.setCenterImage(R.drawable.im_documents_center_image) scanProgress.setCenterImage(R.drawable.im_documents_center_image)
} if(scanType.isDeletedScan){
VALUE_SCAN_TYPE_deleted_documents -> {
title.text = getString(R.string.document_title)
tvScanDescribe.text = getString(R.string.describe_delete_documents) tvScanDescribe.text = getString(R.string.describe_delete_documents)
scanProgress.setCenterImage(R.drawable.im_documents_center_image) }else{
tvScanDescribe.text = getString(R.string.describe_documents)
}
} }
} }
} }
@ -113,7 +93,7 @@ class ScanningActivity : BaseActivity<ActivityScanningBinding>() {
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
val root = Environment.getExternalStorageDirectory() val root = Environment.getExternalStorageDirectory()
ScanManager.scanAllDocuments(this@ScanningActivity, root, type = scanType) ScanManager.scanAllDocuments(this@ScanningActivity, root, fileType = scanType.mediaType)
.flowOn(Dispatchers.IO).collect { .flowOn(Dispatchers.IO).collect {
when (it) { when (it) {
is ScanState.Progress -> { is ScanState.Progress -> {
@ -126,8 +106,6 @@ class ScanningActivity : BaseActivity<ActivityScanningBinding>() {
} }
} }
} }
} }
} }
@ -135,7 +113,7 @@ class ScanningActivity : BaseActivity<ActivityScanningBinding>() {
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
val root = Environment.getExternalStorageDirectory() val root = Environment.getExternalStorageDirectory()
ScanManager.scanHiddenPhotoAsync(this@ScanningActivity, root, type = scanType) ScanManager.scanHiddenPhotoAsync(this@ScanningActivity, root, fileType = scanType.mediaType)
.flowOn(Dispatchers.IO).collect { .flowOn(Dispatchers.IO).collect {
when (it) { when (it) {
is ScanState.Progress -> { is ScanState.Progress -> {
@ -188,12 +166,13 @@ class ScanningActivity : BaseActivity<ActivityScanningBinding>() {
linearCounts.isVisible = false linearCounts.isVisible = false
} else { } else {
finish() finish()
startActivity(Intent(this@ScanningActivity, ScanResultDisplayActivity::class.java).apply { viewModel.updateData(it.result)
putParcelableArrayListExtra( startActivity(
ScanResultDisplayActivity.KEY_SCAN_RESULT, Intent(
it.result this@ScanningActivity,
) ScanResultDisplayActivity::class.java
putExtra(KEY_SCAN_TYPE, scanType) ).apply {
putExtra(Common.KEY_SCAN_TYPE, scanType.value)
}) })
} }
ScanManager.showLog("HiddenScan", "完成: ${it.result.size}") ScanManager.showLog("HiddenScan", "完成: ${it.result.size}")

View File

@ -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<List<ResultData>> = ScanResultRepository.scanData
fun updateData(data: List<ResultData>) {
ScanResultRepository.setScanResult(data)
}
}

View File

@ -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<List<ResultData>> = ScanResultRepository.scanData
fun updateData(data: List<ResultData>) {
ScanResultRepository.setScanResult(data)
}
fun updateSortData(data: List<ResultDataFiles>) {
ScanResultRepository.setSortResult(data)
}
}

View File

@ -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<ActivityPrivacyPolicyBinding>() {
override fun inflateBinding(inflater: LayoutInflater): ActivityPrivacyPolicyBinding =
ActivityPrivacyPolicyBinding.inflate(layoutInflater)
override fun initView() {
super.initView()
}
}

View File

@ -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<ActivitySetUpBinding>() {
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())
}
}

View File

@ -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<ActivityPhotoInfoBinding>() {
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<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable?>,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable?>?,
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)
})
}
}

View File

@ -16,7 +16,7 @@ import com.ux.video.file.filerecovery.databinding.DialogSortBinding
class SortDialogFragment(val onClickSort: (type: Int) -> Unit) : DialogFragment() { class SortDialogFragment(val onClickSort: (type: Int) -> Unit) : DialogFragment() {
private lateinit var binding: DialogSortBinding private lateinit var binding: DialogSortBinding
private var clickType = PhotoSortingActivity.SORT_DESC_DATE private var clickType = SortingActivity.SORT_DESC_DATE
private lateinit var LayoutList: List<CommonLayoutSortItemBinding> private lateinit var LayoutList: List<CommonLayoutSortItemBinding>
override fun onStart() { override fun onStart() {

View File

@ -10,22 +10,22 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.ux.video.file.filerecovery.base.BaseAdapter import com.ux.video.file.filerecovery.base.BaseAdapter
import com.ux.video.file.filerecovery.databinding.PhotoDisplayDateAdapterBinding import com.ux.video.file.filerecovery.databinding.PhotoDisplayDateAdapterBinding
import com.ux.video.file.filerecovery.db.ResultDataFiles import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.db.isThumbnail
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents
import com.ux.video.file.filerecovery.utils.ScanRepository
class PhotoDisplayDateAdapter( import com.ux.video.file.filerecovery.utils.FileType
/**
* 排序页面显示
*/
class SortDisplayAdapter(
mContext: Context, mContext: Context,
var scanType: Int, var fileType: FileType,
var mColumns: Int, var mColumns: Int,
var viewModel: ScanRepository, var viewModel: SortingViewModel,
var onSelectedUpdate: (resultDataFiles: ResultDataFiles, isAdd: Boolean) -> Unit, var onSelectedUpdate: (resultDataFiles: ResultDataFiles, isAdd: Boolean) -> Unit,
var clickItem: (item: ResultDataFiles) -> Unit var clickItem: (item: ResultDataFiles) -> Unit
) : ) :
BaseAdapter<Pair<String, List<ResultDataFiles>>, PhotoDisplayDateAdapterBinding>(mContext) { BaseAdapter<Pair<String, List<ResultDataFiles>>, PhotoDisplayDateAdapterBinding>(mContext) {
private var allSelected: Boolean? = null private var allSelected: Boolean? = null
override fun getViewBinding(parent: ViewGroup): PhotoDisplayDateAdapterBinding = override fun getViewBinding(parent: ViewGroup): PhotoDisplayDateAdapterBinding =
@ -41,7 +41,7 @@ class PhotoDisplayDateAdapter(
*/ */
fun getTotalChildCount(hideThumbnails: Boolean): Int { fun getTotalChildCount(hideThumbnails: Boolean): Int {
if (hideThumbnails) { if (hideThumbnails) {
return data.sumOf { it.second.filter { !it.isThumbnail }.size } return data.sumOf { it.second.filter { !it.isThumbnail() }.size }
} else { } else {
return data.sumOf { it.second.size } return data.sumOf { it.second.size }
} }
@ -80,9 +80,9 @@ class PhotoDisplayDateAdapter(
holder.vb.run { holder.vb.run {
item.run { item.run {
val (date, files) = item val (date, files) = item
val childAdapter = PhotoDisplayDateChildAdapter( val childAdapter = SortDisplayChildAdapter(
mContext, mContext,
scanType, fileType,
mColumns, mColumns,
viewModel, viewModel,
{ resultPhotosFiles, addOrRemove, isDateAllSelected -> { resultPhotosFiles, addOrRemove, isDateAllSelected ->
@ -108,8 +108,8 @@ class PhotoDisplayDateAdapter(
textChildCounts.text = "(${files.size})" textChildCounts.text = "(${files.size})"
recyclerChild.apply { recyclerChild.apply {
layoutManager = when (scanType) { layoutManager = when (fileType) {
Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { FileType.AUDIO, FileType.DOCUMENT -> {
LinearLayoutManager(context) LinearLayoutManager(context)
} }

View File

@ -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.OneAudioItemBinding
import com.ux.video.file.filerecovery.databinding.OneDocumentsItemBinding import com.ux.video.file.filerecovery.databinding.OneDocumentsItemBinding
import com.ux.video.file.filerecovery.db.ResultDataFiles 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.recovery.ui.recoveryphoto.RecoveryPhotoViewModel
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.CustomTextView import com.ux.video.file.filerecovery.utils.CustomTextView
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
import com.ux.video.file.filerecovery.utils.FileType
import com.ux.video.file.filerecovery.utils.ScanManager import com.ux.video.file.filerecovery.utils.ScanManager
import com.ux.video.file.filerecovery.utils.ScanRepository
class PhotoDisplayDateChildAdapter(
/**
* 排序页面显示 已经恢复的文件页面显示
*/
class SortDisplayChildAdapter(
mContext: Context, mContext: Context,
var scanType: Int, var fileType: FileType,
var mColumns: Int, var mColumns: Int,
var viewModel: ViewModel? = null, var viewModel: ViewModel? = null,
/** /**
@ -72,12 +78,12 @@ class PhotoDisplayDateChildAdapter(
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
when (scanType) { when (fileType) {
Common.VALUE_SCAN_TYPE_audio, Common.VALUE_SCAN_TYPE_deleted_audio -> { FileType.AUDIO -> {
return TYPE_AUDIO return TYPE_AUDIO
} }
Common.VALUE_SCAN_TYPE_documents, Common.VALUE_SCAN_TYPE_deleted_documents -> { FileType.DOCUMENT -> {
return TYPE_DOCUMENTS return TYPE_DOCUMENTS
} }
@ -172,20 +178,10 @@ class PhotoDisplayDateChildAdapter(
item.run { item.run {
holder.vb.let { holder.vb.let {
it.textName.text = name it.textName.text = name
it.textDuration.text = Common.formatDuration(duration) it.textDuration.text = Common.formatDuration(duration())
it.textSize.text = sizeString it.textSize.text = sizeString
initAudioDocuments(it.imageSelect, this) 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 { it.constraintLayout.setOnClickListener {
clickItem(this) clickItem(this)
} }
@ -200,7 +196,7 @@ class PhotoDisplayDateChildAdapter(
it.textName.text = name it.textName.text = name
it.textDate.text = Common.getItemMonthDay(lastModified) it.textDate.text = Common.getItemMonthDay(lastModified)
it.textSize.text = sizeString it.textSize.text = sizeString
targetFile?.let { file -> targetFile().let { file ->
it.imageIcon.setImageResource(Common.getFileIconRes(file)) it.imageIcon.setImageResource(Common.getFileIconRes(file))
} }
@ -235,11 +231,11 @@ class PhotoDisplayDateChildAdapter(
private fun initAudioDocuments(imageSelect: ImageView, item: ResultDataFiles) { private fun initAudioDocuments(imageSelect: ImageView, item: ResultDataFiles) {
viewModel?.let { viewModel?.let {
if (it is ScanRepository) { if (it is SortingViewModel) {
it.checkIsSelect(item) it.checkIsSelect(item)
} else { } else {
it as RecoveryPhotoViewModel it as RecoveryPhotoViewModel
it.checkIsSelect(item) it.checkIsSelect(fileType, item)
}.let { isSelected -> }.let { isSelected ->
imageSelect.isSelected = isSelected == true imageSelect.isSelected = isSelected == true
addOrRemove(item, isSelected == true) addOrRemove(item, isSelected == true)
@ -265,31 +261,21 @@ class PhotoDisplayDateChildAdapter(
) { ) {
item.run { item.run {
initAudioDocuments(imageSelectStatus, this) 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 textSize.text = sizeString
imageType.setImageResource( imageType.setImageResource(
when (scanType) { when (fileType) {
Common.VALUE_SCAN_TYPE_photo, Common.VALUE_SCAN_TYPE_deleted_photo -> R.drawable.icon_type_photo FileType.PHOTO -> R.drawable.icon_type_photo
Common.VALUE_SCAN_TYPE_video, Common.VALUE_SCAN_TYPE_deleted_video -> R.drawable.icon_type_video FileType.VIDEO -> R.drawable.icon_type_video
else -> R.drawable.icon_type_photo else -> R.drawable.icon_type_photo
} }
) )
Glide.with(mContext) Glide.with(mContext)
.load(targetFile) .load(targetFile())
.apply( .apply(
RequestOptions() RequestOptions()
.transform(CenterCrop(), RoundedCorners(8.dpToPx(mContext))) .transform(CenterCrop(), RoundedCorners(8.dpToPx(mContext)))
) )
.error(R.drawable.photo_place_holder)
.listener(object : RequestListener<Drawable> { .listener(object : RequestListener<Drawable> {
override fun onLoadFailed( override fun onLoadFailed(
e: GlideException?, e: GlideException?,
@ -299,7 +285,7 @@ class PhotoDisplayDateChildAdapter(
): Boolean { ): Boolean {
ScanManager.showLog( ScanManager.showLog(
"加载图片", "加载图片",
"-------path = ${path} file=${targetFile}" "-------path = ${path} e=${e?.message}"
) )
return false return false
} }

View File

@ -6,8 +6,8 @@ import android.text.TextWatcher
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.activity.viewModels
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager 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.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityPhotoSortingBinding import com.ux.video.file.filerecovery.databinding.ActivityPhotoSortingBinding
import com.ux.video.file.filerecovery.db.ResultDataFiles import com.ux.video.file.filerecovery.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.success.RecoverySuccessActivity
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE
import com.ux.video.file.filerecovery.utils.Common.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.Common.setItemSelect
import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterByDuration 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.filterThumbnailsAsync
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRange import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRange
import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRangeList import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinDateRangeList
import com.ux.video.file.filerecovery.utils.ExtendFunctions.getParcelableArrayListExtraCompat
import com.ux.video.file.filerecovery.utils.ExtendFunctions.kbToBytes 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.mbToBytes
import com.ux.video.file.filerecovery.utils.ExtendFunctions.minutesToMillisecond 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.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.GridSpacingItemDecoration
import com.ux.video.file.filerecovery.utils.ScanRepository import com.ux.video.file.filerecovery.utils.ScanType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.Date import java.util.Date
class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() { class SortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
companion object { companion object {
//指定文件夹下的所有文件 //指定文件夹下的所有文件
@ -67,14 +60,14 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
val SORT_DESC_DATE = 3 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 sortDialogFragment: SortDialogFragment? = null
private var columns = 3 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 private var dialogCustomerDateStart: DatePickerDialogFragment? = null
@ -118,20 +111,86 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
private lateinit var sizeFilterItemArray: Array<String> private lateinit var sizeFilterItemArray: Array<String>
private val viewModel: SortingViewModel by viewModels()
private lateinit var list: List<ResultDataFiles>
// 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() { override fun initData() {
super.initData() super.initData()
scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) val intExtra = intent.getIntExtra(Common.KEY_FILE_TYPE, FileType.PHOTO.value)
val list: ArrayList<ResultDataFiles>? =
intent.getParcelableArrayListExtraCompat(KEY_PHOTO_FOLDER_FILE) fileType = FileType.from(intExtra)!!
mItemDecoration = mItemDecoration =
GridSpacingItemDecoration(columns, Common.itemSpacing, Common.horizontalSpacing) GridSpacingItemDecoration(columns, Common.itemSpacing, Common.horizontalSpacing)
updateButtonCounts(0) 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 -> viewModel.selectedLiveData.observe(this) { selectedSet ->
allSelectedSetList = selectedSet allSelectedSetList = selectedSet
@ -144,64 +203,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
updateCurrentIsAllSelectStatus() updateCurrentIsAllSelectStatus()
} }
setScanTypeView() 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<ActivityPhotoSortingBinding>() {
imageViewBack.setOnClickListener { finish() } imageViewBack.setOnClickListener { finish() }
switchHideThumbnails.setOnCheckedChangeListener { _, isChecked -> switchHideThumbnails.setOnCheckedChangeListener { _, isChecked ->
when (recyclerView.adapter) { when (recyclerView.adapter) {
is PhotoDisplayDateAdapter -> { is SortDisplayAdapter -> {
lifecycleScope.launch { lifecycleScope.launch {
dateAdapter?.run { dateAdapter?.run {
initGetCurrentDateList().let { list -> initGetCurrentDateList().let { list ->
@ -225,7 +227,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
is PhotoDisplayDateChildAdapter -> { is SortDisplayChildAdapter -> {
lifecycleScope.launch { lifecycleScope.launch {
sizeSortAdapter?.run { sizeSortAdapter?.run {
initGetCurrentSizeList().let { initGetCurrentSizeList().let {
@ -336,7 +338,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
tvSelectAll.setOnClickListener { tvSelectAll.setOnClickListener {
it.isSelected = !it.isSelected it.isSelected = !it.isSelected
when (binding.recyclerView.adapter) { when (binding.recyclerView.adapter) {
is PhotoDisplayDateAdapter -> { is SortDisplayAdapter -> {
dateAdapter?.setAllSelected(it.isSelected) dateAdapter?.setAllSelected(it.isSelected)
dateAdapter?.getCurrentData()?.let { dateAdapter?.getCurrentData()?.let {
it as List<Pair<String, List<ResultDataFiles>>> it as List<Pair<String, List<ResultDataFiles>>>
@ -346,7 +348,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
is PhotoDisplayDateChildAdapter -> { is SortDisplayChildAdapter -> {
sizeSortAdapter?.setAllSelected(it.isSelected) sizeSortAdapter?.setAllSelected(it.isSelected)
sizeSortAdapter?.getCurrentData()?.let { sizeSortAdapter?.getCurrentData()?.let {
it as List<ResultDataFiles> it as List<ResultDataFiles>
@ -367,8 +369,9 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
*/ */
private fun setScanTypeView() { private fun setScanTypeView() {
binding.run { binding.run {
when (scanType) { fileType?.let {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { when (it) {
FileType.PHOTO -> {
titleSize.text = getString(R.string.size) titleSize.text = getString(R.string.size)
filterLayoutLinearlayout.isVisible = true filterLayoutLinearlayout.isVisible = true
relativeThumbnails.isVisible = true relativeThumbnails.isVisible = true
@ -376,7 +379,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
sizeFilterItemArray = resources.getStringArray(R.array.filter_size_photo) sizeFilterItemArray = resources.getStringArray(R.array.filter_size_photo)
} }
VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> { FileType.VIDEO -> {
titleSize.text = getString(R.string.duration) titleSize.text = getString(R.string.duration)
filterLayoutLinearlayout.isVisible = true filterLayoutLinearlayout.isVisible = true
relativeThumbnails.isVisible = false relativeThumbnails.isVisible = false
@ -385,7 +388,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
resources.getStringArray(R.array.filter_duration_video_audio) resources.getStringArray(R.array.filter_duration_video_audio)
} }
VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> { FileType.AUDIO -> {
titleSize.text = getString(R.string.duration) titleSize.text = getString(R.string.duration)
filterLayoutLinearlayout.isVisible = false filterLayoutLinearlayout.isVisible = false
relativeThumbnails.isVisible = false relativeThumbnails.isVisible = false
@ -395,7 +398,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
initSearch() initSearch()
} }
VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { FileType.DOCUMENT -> {
titleSize.text = getString(R.string.size) titleSize.text = getString(R.string.size)
filterLayoutLinearlayout.isVisible = false filterLayoutLinearlayout.isVisible = false
relativeThumbnails.isVisible = false relativeThumbnails.isVisible = false
@ -403,6 +406,9 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
sizeFilterItemArray = resources.getStringArray(R.array.filter_documents_size) sizeFilterItemArray = resources.getStringArray(R.array.filter_documents_size)
initSearch() initSearch()
} }
}
} }
} }
@ -413,8 +419,8 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
binding.tvSelectCounts.text = it.toString() binding.tvSelectCounts.text = it.toString()
updateButtonCounts(it) updateButtonCounts(it)
when (binding.recyclerView.adapter) { when (binding.recyclerView.adapter) {
is PhotoDisplayDateAdapter -> { is SortDisplayAdapter -> {
val adapter = binding.recyclerView.adapter as PhotoDisplayDateAdapter val adapter = binding.recyclerView.adapter as SortDisplayAdapter
if (it > 0) { if (it > 0) {
binding.tvSelectAll.isSelected = it == adapter.getTotalChildCount(false) binding.tvSelectAll.isSelected = it == adapter.getTotalChildCount(false)
} else { } else {
@ -423,8 +429,8 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
is PhotoDisplayDateChildAdapter -> { is SortDisplayChildAdapter -> {
val adapter = binding.recyclerView.adapter as PhotoDisplayDateChildAdapter val adapter = binding.recyclerView.adapter as SortDisplayChildAdapter
if (it > 0) { if (it > 0) {
binding.tvSelectAll.isSelected = binding.tvSelectAll.isSelected =
it == adapter.itemCount it == adapter.itemCount
@ -448,7 +454,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
currentDateList = null currentDateList = null
binding.tvThumbnailCounts.text = 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<ActivityPhotoSortingBinding>() {
currentDateList = currentList currentDateList = currentList
currentSizeList = null currentSizeList = null
val totalSelectedCount = currentList.sumOf { pair -> val totalSelectedCount = currentList.sumOf { pair ->
pair.second.filter { it.isThumbnail }.size pair.second.filter { it.isThumbnail() }.size
} }
binding.tvThumbnailCounts.text = binding.tvThumbnailCounts.text =
getString(R.string.hide_thumbnails, totalSelectedCount) getString(R.string.hide_thumbnails, totalSelectedCount)
@ -474,7 +480,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
private fun setDateAdapter() { private fun setDateAdapter() {
binding.recyclerView.run { binding.recyclerView.run {
adapter = dateAdapter?.apply { setColumns(columns) } adapter = dateAdapter?.apply { setColumns(columns) }
layoutManager = LinearLayoutManager(this@PhotoSortingActivity) layoutManager = LinearLayoutManager(this@SortingActivity)
setPadding(0, 0, 0, 70.dpToPx(context)) setPadding(0, 0, 0, 70.dpToPx(context))
clipToPadding = false clipToPadding = false
} }
@ -484,8 +490,8 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
binding.recyclerView.run { binding.recyclerView.run {
val aPx = 16.dpToPx(context) val aPx = 16.dpToPx(context)
val bottom = 70.dpToPx(context) val bottom = 70.dpToPx(context)
when (scanType) { when (fileType) {
VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio,VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { FileType.AUDIO, FileType.DOCUMENT-> {
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
setPadding(aPx, 0, 0, bottom) setPadding(aPx, 0, 0, bottom)
} }
@ -513,7 +519,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
setItemSelect(it as LinearLayout, true) setItemSelect(it as LinearLayout, true)
resources.getStringArray(R.array.filter_date).let { data -> resources.getStringArray(R.array.filter_date).let { data ->
filterDatePopupWindows = filterDatePopupWindows ?: DateFilterPopupWindows( filterDatePopupWindows = filterDatePopupWindows ?: DateFilterPopupWindows(
this@PhotoSortingActivity, this@SortingActivity,
0, 0,
{ clickValue, showDialog -> { clickValue, showDialog ->
when (clickValue) { when (clickValue) {
@ -576,7 +582,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
setItemSelect(it as LinearLayout, true) setItemSelect(it as LinearLayout, true)
sizeFilterItemArray.let { data -> sizeFilterItemArray.let { data ->
filterSizePopupWindows = filterSizePopupWindows ?: FilterPopupWindows( filterSizePopupWindows = filterSizePopupWindows ?: FilterPopupWindows(
this@PhotoSortingActivity, this@SortingActivity,
data, data,
0, 0,
{ clickValue -> { clickValue ->
@ -598,7 +604,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
setItemSelect(it as LinearLayout, true) setItemSelect(it as LinearLayout, true)
resources.getStringArray(R.array.filter_layout).let { data -> resources.getStringArray(R.array.filter_layout).let { data ->
filterLayoutPopupWindows = filterLayoutPopupWindows ?: FilterPopupWindows( filterLayoutPopupWindows = filterLayoutPopupWindows ?: FilterPopupWindows(
this@PhotoSortingActivity, this@SortingActivity,
data, data,
1, 1,
{ clickValue -> { clickValue ->
@ -608,13 +614,13 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
data[2] -> columns = 4 data[2] -> columns = 4
} }
when (binding.recyclerView.adapter) { when (binding.recyclerView.adapter) {
is PhotoDisplayDateAdapter -> { is SortDisplayAdapter -> {
dateAdapter?.setColumns(columns) dateAdapter?.setColumns(columns)
} }
is PhotoDisplayDateChildAdapter -> { is SortDisplayChildAdapter -> {
binding.recyclerView.layoutManager = binding.recyclerView.layoutManager =
GridLayoutManager(this@PhotoSortingActivity, columns) GridLayoutManager(this@SortingActivity, columns)
sizeSortAdapter?.setColumns(columns) sizeSortAdapter?.setColumns(columns)
} }
} }
@ -654,10 +660,10 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
private fun startFilter() { private fun startFilter() {
Common.showLog("--------------开始筛选") Common.showLog("--------------开始筛选")
val filterSizeCovert = filterSizeCovert(scanType, filterSize) val filterSizeCovert = filterSizeCovert(fileType, filterSize)
when (binding.recyclerView.adapter) { when (binding.recyclerView.adapter) {
//当前是时间排序 //当前是时间排序
is PhotoDisplayDateAdapter -> { is SortDisplayAdapter -> {
//确定当前排序 //确定当前排序
val list = if (sortReverse) sortByDateReverse else sortedByDatePositive val list = if (sortReverse) sortByDateReverse else sortedByDatePositive
list.filterWithinDateRange( list.filterWithinDateRange(
@ -665,8 +671,8 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
startDate = if (filterDate == FILTER_DATE_CUSTOMER) filterStartDate else null, startDate = if (filterDate == FILTER_DATE_CUSTOMER) filterStartDate else null,
endDate = if (filterDate == FILTER_DATE_CUSTOMER) filterEndDate else null endDate = if (filterDate == FILTER_DATE_CUSTOMER) filterEndDate else null
).run { ).run {
when (scanType) { when (fileType) {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { FileType.PHOTO,FileType.DOCUMENT -> {
filterBySize(filterSizeCovert.first, filterSizeCovert.second) filterBySize(filterSizeCovert.first, filterSizeCovert.second)
} }
@ -684,15 +690,15 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
//当前是大小排序 //当前是大小排序
is PhotoDisplayDateChildAdapter -> { is SortDisplayChildAdapter -> {
val list = if (sortReverse) sortBySizeBigToSmall else sortBySizeSmallToBig val list = if (sortReverse) sortBySizeBigToSmall else sortBySizeSmallToBig
list.filterWithinDateRangeList( list.filterWithinDateRangeList(
filterDate, filterDate,
startDate = if (filterDate == FILTER_DATE_CUSTOMER) filterStartDate else null, startDate = if (filterDate == FILTER_DATE_CUSTOMER) filterStartDate else null,
endDate = if (filterDate == FILTER_DATE_CUSTOMER) filterEndDate else null endDate = if (filterDate == FILTER_DATE_CUSTOMER) filterEndDate else null
).run { ).run {
when (scanType) { when (fileType) {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo, VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { FileType.PHOTO,FileType.DOCUMENT-> {
filterBySizeList(filterSizeCovert.first, filterSizeCovert.second) filterBySizeList(filterSizeCovert.first, filterSizeCovert.second)
} }
@ -736,9 +742,9 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
private fun filterSizeCovert(scanType: Int, filterSize: String): Pair<Long, Long> { private fun filterSizeCovert(fileType: FileType, filterSize: String): Pair<Long, Long> {
when (scanType) { when (fileType) {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { FileType.PHOTO -> {
val stringArray = resources.getStringArray(R.array.filter_size_photo) val stringArray = resources.getStringArray(R.array.filter_size_photo)
return when (filterSize) { return when (filterSize) {
stringArray[0] -> Pair(-1L, -1L) stringArray[0] -> Pair(-1L, -1L)
@ -749,7 +755,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
} }
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) val stringArray = resources.getStringArray(R.array.filter_duration_video_audio)
return when (filterSize) { return when (filterSize) {
stringArray[0] -> Pair(-1L, -1L) stringArray[0] -> Pair(-1L, -1L)
@ -761,7 +767,7 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
} }
VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { FileType.DOCUMENT-> {
val stringArray = resources.getStringArray(R.array.filter_documents_size) val stringArray = resources.getStringArray(R.array.filter_documents_size)
return when (filterSize) { return when (filterSize) {
stringArray[0] -> Pair(-1L, -1L) stringArray[0] -> Pair(-1L, -1L)
@ -773,7 +779,6 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
} }
} }
return Pair(-1L, -1L)
} }
/** /**
@ -846,9 +851,10 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
* @param type 0 恢复 1 删除 * @param type 0 恢复 1 删除
*/ */
private fun complete(number: Int, type: Int) { 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_COUNT, number)
putExtra(RecoverySuccessActivity.KEY_SUCCESS_TYPE, type) putExtra(RecoverySuccessActivity.KEY_SUCCESS_TYPE, type)
putExtra(Common.KEY_FILE_TYPE, fileType.value)
}) })
if (type == 1) { if (type == 1) {
lifecycleScope.launch { lifecycleScope.launch {
@ -1019,11 +1025,11 @@ class PhotoSortingActivity : BaseActivity<ActivityPhotoSortingBinding>() {
private fun checkTypeAfter(onAction: (isDateSort: Boolean) -> Unit) { private fun checkTypeAfter(onAction: (isDateSort: Boolean) -> Unit) {
when (binding.recyclerView.adapter) { when (binding.recyclerView.adapter) {
is PhotoDisplayDateAdapter -> { is SortDisplayAdapter -> {
onAction(true) onAction(true)
} }
is PhotoDisplayDateChildAdapter -> { is SortDisplayChildAdapter -> {
onAction(false) onAction(false)
} }
} }

View File

@ -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.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel 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.db.ResultDataFiles
import com.ux.video.file.filerecovery.result.ScanResultRepository
import com.ux.video.file.filerecovery.utils.Common
class SortingViewModel : ViewModel() {
class ScanRepository : ViewModel() {
// private val _selectedFlow = MutableStateFlow<MutableSet<String>>(mutableSetOf()) // private val _selectedFlow = MutableStateFlow<MutableSet<String>>(mutableSetOf())
// val selectedFlow: StateFlow<MutableSet<String>> = _selectedFlow // val selectedFlow: StateFlow<MutableSet<String>> = _selectedFlow
// //
@ -14,7 +16,7 @@ class ScanRepository : ViewModel() {
// private val _selectedDisplayFlow = MutableStateFlow<MutableSet<String>>(mutableSetOf()) // private val _selectedDisplayFlow = MutableStateFlow<MutableSet<String>>(mutableSetOf())
// val selectedDisplayFlow: StateFlow<MutableSet<String>> = _selectedDisplayFlow // val selectedDisplayFlow: StateFlow<MutableSet<String>> = _selectedDisplayFlow
val sortingData: MutableLiveData<List<ResultDataFiles>> = ScanResultRepository.sortingData
private val _selectedLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet()) private val _selectedLiveData = MutableLiveData<Set<ResultDataFiles>>(emptySet())
val selectedLiveData: LiveData<Set<ResultDataFiles>> = _selectedLiveData val selectedLiveData: LiveData<Set<ResultDataFiles>> = _selectedLiveData

View File

@ -1,36 +1,15 @@
package com.ux.video.file.filerecovery.success package com.ux.video.file.filerecovery.success
import android.content.Intent import android.content.Intent
import android.os.Environment
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.base.BaseActivity
import com.ux.video.file.filerecovery.databinding.ActivityRecoverOrDeletedSuccessBinding 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.MainActivity
import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_AUDIO import com.ux.video.file.filerecovery.recovery.RecoveryActivity
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.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE import com.ux.video.file.filerecovery.utils.FileType
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
class RecoverySuccessActivity : BaseActivity<ActivityRecoverOrDeletedSuccessBinding>() { class RecoverySuccessActivity : BaseActivity<ActivityRecoverOrDeletedSuccessBinding>() {
@ -39,7 +18,7 @@ class RecoverySuccessActivity : BaseActivity<ActivityRecoverOrDeletedSuccessBind
val KEY_SUCCESS_COUNT = "success_count" val KEY_SUCCESS_COUNT = "success_count"
} }
private var scanType: Int = VALUE_SCAN_TYPE_photo private lateinit var fileType: FileType
//0 恢复成功 1 删除成功 //0 恢复成功 1 删除成功
private var successType = 0 private var successType = 0
@ -48,7 +27,10 @@ class RecoverySuccessActivity : BaseActivity<ActivityRecoverOrDeletedSuccessBind
override fun initData() { override fun initData() {
super.initData() super.initData()
scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) val intExtra = intent.getIntExtra(Common.KEY_FILE_TYPE, FileType.PHOTO.value)
fileType = FileType.from(intExtra)!!
successType = intent.getIntExtra(KEY_SUCCESS_TYPE, 0) successType = intent.getIntExtra(KEY_SUCCESS_TYPE, 0)
val counts = intent.getIntExtra(KEY_SUCCESS_COUNT, 0) val counts = intent.getIntExtra(KEY_SUCCESS_COUNT, 0)
@ -58,17 +40,17 @@ class RecoverySuccessActivity : BaseActivity<ActivityRecoverOrDeletedSuccessBind
tvLeft.text = resources.getString(R.string.text_continue) tvLeft.text = resources.getString(R.string.text_continue)
tvRight.text = resources.getString(R.string.view) tvRight.text = resources.getString(R.string.view)
} }
when (scanType) { when (fileType) {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> tvFileType.text = FileType.PHOTO -> tvFileType.text =
resources.getString(R.string.describe_photos) 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) 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) 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) resources.getString(R.string.describe_documents)
} }
when (successType) { when (successType) {
@ -86,7 +68,13 @@ class RecoverySuccessActivity : BaseActivity<ActivityRecoverOrDeletedSuccessBind
bottomBtnRecoverLayout.tvLeft.setOnClickListener { finish()} bottomBtnRecoverLayout.tvLeft.setOnClickListener { finish()}
bottomBtnRecoverLayout.tvRight.setOnClickListener { bottomBtnRecoverLayout.tvRight.setOnClickListener {
//todo 跳转到所有恢复文件页面 finish()
Intent(this@RecoverySuccessActivity,RecoveryActivity::class.java).apply {
putExtra(Common.KEY_FILE_TYPE,fileType.value)
}.let {
startActivity(it)
}
} }
} }

View File

@ -2,6 +2,8 @@ package com.ux.video.file.filerecovery.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.icu.text.SimpleDateFormat import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar import android.icu.util.Calendar
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
@ -11,34 +13,24 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.R
import com.ux.video.file.filerecovery.db.ResultDataFiles import com.ux.video.file.filerecovery.db.ResultDataFiles
import com.ux.video.file.filerecovery.utils.ScanManager.getVideoResolution
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
import kotlin.collections.sortedBy
object Common { object Common {
val itemSpacing = 10 val itemSpacing = 10
val horizontalSpacing = 16 val horizontalSpacing = 16
val KEY_SCAN_TYPE = "scan_type" val KEY_SCAN_TYPE = "key_scan_type"
val VALUE_SCAN_TYPE_photo = 0 val KEY_FILE_TYPE = "key_file_type"
val VALUE_SCAN_TYPE_deleted_photo = 1
val VALUE_SCAN_TYPE_video = 2
val VALUE_SCAN_TYPE_deleted_video = 3
val VALUE_SCAN_TYPE_audio = 4
val VALUE_SCAN_TYPE_deleted_audio = 5
val VALUE_SCAN_TYPE_documents = 6
val VALUE_SCAN_TYPE_deleted_documents = 7
val FILE_TYPE_PHOTO = 0
val FILE_TYPE_VIDEO = 1
val FILE_TYPE_AUDIO = 2
val FILE_TYPE_DOCUMENTS = 3
@ -103,6 +95,7 @@ object Common {
if (filtered.isNotEmpty()) key to filtered else null if (filtered.isNotEmpty()) key to filtered else null
} }
} }
fun searchByNameList( fun searchByNameList(
list: List<ResultDataFiles>, list: List<ResultDataFiles>,
keyword: String keyword: String
@ -112,7 +105,6 @@ object Common {
} }
/** /**
* 格式化文件大小显示 * 格式化文件大小显示
*/ */
@ -261,19 +253,7 @@ object Common {
} }
/**
* 去掉缩略图的集合
*/
suspend fun filterThumbnailsAsync(
originalList: MutableList<Pair<String, List<ResultDataFiles>>>
): List<Pair<String, List<ResultDataFiles>>> = withContext(Dispatchers.Default) {
originalList.asSequence()
.map { (key, files) ->
key to files.asSequence().filter { !it.isThumbnail }.toList()
}
.filter { it.second.isNotEmpty() }
.toList()
}
fun removeSelectedFromList( fun removeSelectedFromList(
@ -294,12 +274,12 @@ object Common {
} }
fun getMediaDuration(filePath: String): Long { fun getMediaDuration(filePath: String): Long {
val retriever = MediaMetadataRetriever() val retriever = MediaMetadataRetriever()
return try { return try {
retriever.setDataSource(filePath) retriever.setDataSource(filePath)
val durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) val durationStr =
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
durationStr?.toLongOrNull() ?: 0L // 单位:毫秒 durationStr?.toLongOrNull() ?: 0L // 单位:毫秒
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@ -323,6 +303,7 @@ object Common {
String.format("%02d:%02d", minutes, seconds) String.format("%02d:%02d", minutes, seconds)
} }
} }
fun getFileMIME(file: File): String { fun getFileMIME(file: File): String {
val ext = file.extension.lowercase() val ext = file.extension.lowercase()
val mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext) val mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)
@ -340,6 +321,7 @@ object Common {
mime == "application/zip" || mime == "application/zip" ||
mime == "application/x-rar-compressed" || mime == "application/x-rar-compressed" ||
mime == "application/x-7z-compressed" -> "archive" mime == "application/x-7z-compressed" -> "archive"
else -> "other" else -> "other"
} }
} }
@ -348,9 +330,11 @@ object Common {
"xapk" to "application/zip", "xapk" to "application/zip",
// 可以继续添加其他自定义扩展名 // 可以继续添加其他自定义扩展名
) )
fun getMimeTypeParts(file: File): String { fun getMimeTypeParts(file: File): String {
val extension = file.extension.lowercase() 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}") showLog("-----------ext=$extension mimeType=${mimeType}")
return if (mimeType != null && mimeType.contains("/")) { return if (mimeType != null && mimeType.contains("/")) {
val parts = mimeType.split("/") val parts = mimeType.split("/")
@ -380,6 +364,7 @@ object Common {
fun getFormatDate(time: Long): String { fun getFormatDate(time: Long): String {
return dateFormat.format(Date(time)) return dateFormat.format(Date(time))
} }
fun getItemMonthDay(time: Long): String { fun getItemMonthDay(time: Long): String {
return itemDateFormat.format(time) return itemDateFormat.format(time)
} }
@ -400,4 +385,117 @@ object Common {
fun showLog(msg: String) { fun showLog(msg: String) {
Log.d("============", msg) 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<File>, 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<Int, Int> {
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 -> {}
}
}
} }

View File

@ -8,6 +8,8 @@ import android.os.Parcelable
import android.util.TypedValue import android.util.TypedValue
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.ux.video.file.filerecovery.db.ResultDataFiles 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.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.Date import java.util.Date
@ -24,6 +26,8 @@ object ExtendFunctions {
} }
inline fun <reified T : Parcelable> Intent.getParcelableArrayListExtraCompat(key: String): ArrayList<T>? { inline fun <reified T : Parcelable> Intent.getParcelableArrayListExtraCompat(key: String): ArrayList<T>? {
extras?.classLoader = T::class.java.classLoader
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableArrayListExtra(key, T::class.java) getParcelableArrayListExtra(key, T::class.java)
} else { } else {
@ -134,7 +138,7 @@ object ExtendFunctions {
maxSize: Long maxSize: Long
): List<ResultDataFiles> { ): List<ResultDataFiles> {
if (minSize == -1L) return this if (minSize == -1L) return this
return this.filter { it.duration in minSize..maxSize } return this.filter { it.duration() in minSize..maxSize }
} }
/** /**
@ -160,7 +164,7 @@ object ExtendFunctions {
): List<Pair<String, List<ResultDataFiles>>> { ): List<Pair<String, List<ResultDataFiles>>> {
if (minSize == -1L) return this if (minSize == -1L) return this
return this.mapNotNull { (date, files) -> return this.mapNotNull { (date, files) ->
val filtered = files.filter { it.duration in minSize..maxSize } val filtered = files.filter { it.duration() in minSize..maxSize }
if (filtered.isNotEmpty()) date to filtered else null if (filtered.isNotEmpty()) date to filtered else null
} }
} }
@ -185,7 +189,7 @@ object ExtendFunctions {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
this@filterThumbnailsAsync.asSequence() this@filterThumbnailsAsync.asSequence()
.mapNotNull { (key, files) -> .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 if (filtered.isNotEmpty()) key to filtered else null
} }
.toList() .toList()
@ -198,7 +202,7 @@ object ExtendFunctions {
suspend fun List<ResultDataFiles>.filterRemoveThumbnailsAsync(): List<ResultDataFiles> = suspend fun List<ResultDataFiles>.filterRemoveThumbnailsAsync(): List<ResultDataFiles> =
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
this@filterRemoveThumbnailsAsync.asSequence() this@filterRemoveThumbnailsAsync.asSequence()
.filter { !it.isThumbnail } // 去掉 isThumbnail = true 的项 .filter { !it.isThumbnail() } // 去掉 isThumbnail = true 的项
.toList() .toList()
} }

View File

@ -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 }
}
}

View File

@ -1,23 +1,17 @@
package com.ux.video.file.filerecovery.utils package com.ux.video.file.filerecovery.utils
import android.content.ContentUris
import android.content.Context import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import android.net.Uri import android.net.Uri
import android.provider.MediaStore
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.text.format.Formatter import android.text.format.Formatter
import android.util.Log import android.util.Log
import com.ux.video.file.filerecovery.db.ResultData import com.ux.video.file.filerecovery.db.ResultData
import com.ux.video.file.filerecovery.db.ResultDataFiles 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.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.currentCoroutineContext
@ -56,7 +50,7 @@ object ScanManager {
context: Context, context: Context,
root: File, maxDepth: Int = 5, root: File, maxDepth: Int = 5,
maxFiles: Int = 5000, maxFiles: Int = 5000,
type: Int fileType: FileType
): Flow<ScanState> = flow { ): Flow<ScanState> = flow {
val result = mutableMapOf<String, MutableList<File>>() val result = mutableMapOf<String, MutableList<File>>()
@ -71,24 +65,7 @@ object ScanManager {
if (file.isDirectory) { if (file.isDirectory) {
scanDocuments(file, depth + 1) scanDocuments(file, depth + 1)
} else { } else {
var fileCheckBoolean: Boolean = false val fileCheckBoolean: Boolean = checkFileFormat(file,fileType)
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)
}
}
if (fileCheckBoolean) { if (fileCheckBoolean) {
val dirName = file.parentFile?.name ?: "Unknown" val dirName = file.parentFile?.name ?: "Unknown"
@ -114,8 +91,7 @@ object ScanManager {
file.length() file.length()
), ),
lastModified = file.lastModified(), lastModified = file.lastModified(),
resolution = getResolution(type, file), fileType = fileType
fileType = getFileType(type)
) )
} }
ResultData(dir, ArrayList(resultDataFilesList)) ResultData(dir, ArrayList(resultDataFilesList))
@ -159,9 +135,9 @@ object ScanManager {
fun scanHiddenPhotoAsync( fun scanHiddenPhotoAsync(
context: Context, context: Context,
root: File, maxDepth: Int = 5, root: File, maxDepth: Int = 5,
maxFiles: Int = 5000, type: Int maxFiles: Int = 5000, fileType: FileType
): Flow<ScanState> = flow { ): Flow<ScanState> = flow {
// scanRecycler(context)
val result = mutableMapOf<String, MutableList<File>>() val result = mutableMapOf<String, MutableList<File>>()
var fileCount = 0 var fileCount = 0
@ -169,46 +145,52 @@ object ScanManager {
suspend fun scanDir(dir: File, depth: Int, insideHidden: Boolean = false) { suspend fun scanDir(dir: File, depth: Int, insideHidden: Boolean = false) {
if (!dir.exists() || !dir.isDirectory) return if (!dir.exists() || !dir.isDirectory) return
if (depth > maxDepth || fileCount >= maxFiles) return if (depth > maxDepth || fileCount >= maxFiles) return
val skipDirs = setOf(
"Android",
"obb",
"data",
".thumbnails",
".cache"
)
if (skipDirs.contains(dir.name)) return
showLog("HiddenScan", "${dir.name} 111111") showLog("HiddenScan", "${dir.name} 111111")
dir.listFiles()?.forEach { file -> dir.listFiles()?.forEach { file ->
if (file.isDirectory) { if (file.isDirectory) {
val isHidden = file.name.startsWith(".") val isHidden = file.name.startsWith(".")
scanDir(file, depth + 1, insideHidden = insideHidden || isHidden) scanDir(file, depth + 1, insideHidden = insideHidden || isHidden)
} else { } else {
val fileCheckBoolean: Boolean = checkFileFormat(file,fileType)
if (insideHidden) { 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) { if (fileCheckBoolean) {
val dirName = file.parentFile?.name ?: "Unknown" val dirName = file.parentFile?.name ?: "Unknown"
ScanManager.showLog("HiddenScan", "${dirName} 22222") ScanManager.showLog("HiddenScan", "${dirName} 22222 ${file.name}")
val list = result.getOrPut(dirName) { mutableListOf() } val list = result.getOrPut(dirName) { mutableListOf() }
list.add(file) list.add(file)
fileCount++ fileCount++
emit(ScanState.Progress(fileCount, file.absolutePath)) 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) scanDir(root, depth = 0)
ScanManager.showLog("HiddenScan", " 3333") ScanManager.showLog("HiddenScan", " 44444444")
val map = result.map { (dir, files) -> val map = result.map { (dir, files) ->
val resultDataFilesList = files.map { file -> val resultDataFilesList = files.map { file ->
ResultDataFiles( ResultDataFiles(
@ -220,8 +202,7 @@ object ScanManager {
file.length() file.length()
), ),
lastModified = file.lastModified(), lastModified = file.lastModified(),
resolution = getResolution(type, file), fileType = fileType
fileType = getFileType(type)
) )
} }
@ -231,17 +212,63 @@ object ScanManager {
} }
private fun getFileType(scanType: Int): Int { private fun checkFileFormat(file: File, fileType: FileType): Boolean {
return when (scanType) { return when (fileType) {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> 0 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 FileType.AUDIO -> {
VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> 3 isFormatFile(file, AUDIO_FILE)
else -> 3 }
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 { private fun getFileSizeByMediaStore(context: Context, file: File): Long {
val uri = Uri.fromFile(file) val uri = Uri.fromFile(file)
@ -255,23 +282,24 @@ object ScanManager {
return file.length() // fallback return file.length() // fallback
} }
private fun getResolution(type: Int, file: File): String { // private fun getResolution(type: Int, file: File): String {
return when (type) { // return when (type) {
VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { // VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> {
getImageSize(file).run { // getImageSize(file).run {
"$first*$second" // "$first*$second"
} // }
} // }
//
VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> getVideoResolution(file.path).run { // VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> getVideoResolution(file.path).run {
"$first*$second" // "$first*$second"
} // }
//
else -> "" // else -> ""
} // }
} // }
private fun isFormatFile(file: File, types: List<String>): Boolean { private fun isFormatFile(file: File, types: List<String>): Boolean {
if (!file.exists() || file.length() <= 0) return false
val ext = file.extension.lowercase() val ext = file.extension.lowercase()
return types.contains(ext) return types.contains(ext)
} }
@ -284,6 +312,7 @@ object ScanManager {
Log.d(tag, msg) Log.d(tag, msg)
} }
fun isValidImage(file: File): Boolean { fun isValidImage(file: File): Boolean {
if (!file.exists() || file.length() <= 0) return false if (!file.exists() || file.length() <= 0) return false
return try { return try {

View File

@ -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 }
}
}

View File

@ -107,4 +107,9 @@ class PlayMediaManager(var context: Context, var mediaFile: File, var playView:
} }
}) })
} }
fun stopPlayAudio(){
player.stop()
}
} }

View File

@ -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.databinding.ActivityVideoPlayBinding
import com.ux.video.file.filerecovery.sort.RecoverOrDeleteManager import com.ux.video.file.filerecovery.sort.RecoverOrDeleteManager
import com.ux.video.file.filerecovery.db.ResultDataFiles 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.success.RecoverySuccessActivity
import com.ux.video.file.filerecovery.utils.Common import com.ux.video.file.filerecovery.utils.Common
import com.ux.video.file.filerecovery.utils.FileType
class VideoPlayActivity : BaseActivity<ActivityVideoPlayBinding>() { class VideoPlayActivity : BaseActivity<ActivityVideoPlayBinding>() {
@ -48,7 +50,7 @@ class VideoPlayActivity : BaseActivity<ActivityVideoPlayBinding>() {
binding.run { binding.run {
myData?.let { resultPhotosFiles-> myData?.let { resultPhotosFiles->
imageBack.setOnClickListener { finish() } imageBack.setOnClickListener { finish() }
resultPhotosFiles.targetFile?.let { resultPhotosFiles.targetFile().let {
PlayMediaManager(context = this@VideoPlayActivity, mediaFile = it, playView = playerView, PlayMediaManager(context = this@VideoPlayActivity, mediaFile = it, playView = playerView,
seekBar = seekBar, playBtn = playImage, onUpdateProgress = { current,total-> seekBar = seekBar, playBtn = playImage, onUpdateProgress = { current,total->
textTimeCurrent.text = current textTimeCurrent.text = current
@ -141,7 +143,7 @@ class VideoPlayActivity : BaseActivity<ActivityVideoPlayBinding>() {
myData?.let { myData?.let {
player = ExoPlayer.Builder(this).build() player = ExoPlayer.Builder(this).build()
binding.playerView.player = player 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 { player.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) { override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState) super.onPlaybackStateChanged(playbackState)
@ -177,6 +179,7 @@ class VideoPlayActivity : BaseActivity<ActivityVideoPlayBinding>() {
startActivity(Intent(this@VideoPlayActivity, RecoverySuccessActivity::class.java).apply { startActivity(Intent(this@VideoPlayActivity, RecoverySuccessActivity::class.java).apply {
putExtra(RecoverySuccessActivity.KEY_SUCCESS_COUNT, number) putExtra(RecoverySuccessActivity.KEY_SUCCESS_COUNT, number)
putExtra(RecoverySuccessActivity.KEY_SUCCESS_TYPE, type) putExtra(RecoverySuccessActivity.KEY_SUCCESS_TYPE, type)
putExtra(Common.KEY_FILE_TYPE, FileType.VIDEO.value)
}) })
} }
} }

View File

@ -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<ActivitySplashBinding>() {
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()
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp"/>
<solid android:color="@color/dialog_delete_success_color"/>
</shape>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="50dp" />
<gradient
android:angle="180"
android:endColor="@color/welcome_enter_start_color"
android:startColor="@color/welcome_enter_end_color" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 KiB

View File

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

View File

@ -27,6 +27,7 @@
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:src="@drawable/ic_setting" android:src="@drawable/ic_setting"
android:id="@+id/im_setting"
app:layout_constraintBottom_toBottomOf="@id/tv_title" app:layout_constraintBottom_toBottomOf="@id/tv_title"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_title" /> app:layout_constraintTop_toTopOf="@id/tv_title" />
@ -186,7 +187,7 @@
android:id="@+id/layout_recovery" android:id="@+id/layout_recovery"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="60dp" android:layout_height="60dp"
android:layout_marginStart="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@drawable/main_type_bg" android:background="@drawable/main_type_bg"
android:gravity="center_vertical" android:gravity="center_vertical"
@ -219,6 +220,7 @@
android:layout_height="60dp" android:layout_height="60dp"
android:layout_marginStart="11dp" android:layout_marginStart="11dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:visibility="gone"
android:background="@drawable/main_type_bg" android:background="@drawable/main_type_bg"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
@ -243,66 +245,8 @@
app:fontType="bold" /> app:fontType="bold" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/layout_wchat">
<Button
android:id="@+id/btn_permission"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="管理所有文件的权限申请" />
<Button
android:id="@+id/btn_scan_all_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="扫描所有图片" />
<Button
android:id="@+id/btn_scan_all_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="扫描所有视频" />
<Button
android:id="@+id/btn_scan_all_audio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="扫描所有音频" />
<Button
android:id="@+id/btn_scan_all_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="扫描所有文件" />
</LinearLayout>
<RelativeLayout
android:id="@+id/layout_permission"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name" />
<Button
android:id="@+id/allow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="50dp"
android:text="Allow" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

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

View File

@ -1,17 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main" android:id="@+id/main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".documents.DocumentsScanResultActivity"> tools:context=".settings.PrivacyPolicyActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".settings.SetupActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="44dp"
android:gravity="center_vertical">
<ImageView
android:id="@+id/image_view_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingHorizontal="13dp"
android:paddingVertical="14dp"
android:src="@drawable/black_return" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/settings"
android:textColor="@color/main_title"
android:textSize="16sp"
app:fontType="bold" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/layout_share_app"
android:layout_width="match_parent"
android:layout_height="65dp"
android:paddingStart="16dp"
android:paddingEnd="0dp">
<ImageView
android:id="@+id/im_share_app"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:src="@drawable/icon_share_app" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:layout_toEndOf="@id/im_share_app"
android:text="@string/share_app"
android:textColor="@color/main_title"
android:textSize="14sp"
app:fontType="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="@color/dividing_line_color" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/layout_privacy_policy"
android:layout_width="match_parent"
android:layout_height="65dp"
android:paddingStart="16dp"
android:paddingEnd="0dp">
<ImageView
android:id="@+id/im_privacy_policy"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:src="@drawable/icon_privacy_policy" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:layout_toEndOf="@id/im_privacy_policy"
android:text="@string/privacy_policy"
android:textColor="@color/main_title"
android:textSize="14sp"
app:fontType="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentBottom="true"
android:background="@color/dividing_line_color" />
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/welcome_bg"
tools:context=".welcome.SplashActivity">
<com.ux.video.file.filerecovery.utils.CustomTextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
android:layout_marginTop="87dp"
android:text="@string/welcome_name"
android:textColor="@color/main_title"
android:textSize="48sp"
app:fontType="alimama"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
android:layout_marginTop="15dp"
android:text="@string/welcome_sub_text"
android:textColor="@color/main_sub_title"
android:textSize="16sp"
app:fontType="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_name" />
<com.ux.video.file.filerecovery.utils.CustomTextView
android:id="@+id/text_enter"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="60dp"
android:layout_marginBottom="80dp"
android:background="@drawable/bg_welcome_enter"
android:gravity="center"
android:text="@string/enter"
android:textColor="@color/white"
android:textSize="18sp"
app:fontType="alimama"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:background="@drawable/bg_000000_10"
android:paddingHorizontal="16dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon_checkmark_16dp"/>
<com.ux.video.file.filerecovery.utils.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/white"
android:layout_marginStart="5dp"
android:text="@string/deleted_success"/>
</LinearLayout>

View File

@ -11,7 +11,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="@color/selector_recovery_file_tab_layout_title" android:textColor="@color/selector_recovery_file_tab_layout_title"
android:textSize="15sp" android:textSize="13sp"
android:gravity="center" android:gravity="center"
app:fontType="bold" /> app:fontType="bold" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- <style name="Theme.FileRecovery" parent="Theme.SplashScreen">-->
<!-- <item name="windowSplashScreenBackground">@color/black</item>-->
<!-- <item name="windowSplashScreenAnimatedIcon">@mipmap/recovery</item>-->
<!-- <item name="postSplashScreenTheme">@style/Theme.FileRecovery</item>-->
<!-- </style>-->
<style name="Base.Theme.FileRecovery" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
<item name="android:windowBackground">@color/white</item>
</style>
</resources>

View File

@ -26,5 +26,8 @@
<color name="view_div_color">#D9D9D9</color> <color name="view_div_color">#D9D9D9</color>
<color name="bg_seekbar_video_play">#99F2F2F7</color> <color name="bg_seekbar_video_play">#99F2F2F7</color>
<color name="dialog_delete_success_color">#98000000</color>
<color name="welcome_enter_start_color">#70ABFF</color>
<color name="welcome_enter_end_color">#326EFF</color>
</resources> </resources>

View File

@ -1,10 +1,13 @@
<resources> <resources>
<string name="app_name">File Recovery</string> <string name="app_name">File Recovery Tool</string>
<string name="scan_all_file">Scan all files</string> <string name="scan_all_file">Scan all files</string>
<string name="scan_deleted_files">Scan deleted files</string> <string name="scan_deleted_files">Scan deleted files</string>
<string name="select_all">Select all</string> <string name="select_all">Select all</string>
<string name="select">Select</string> <string name="select">Select</string>
<string name="deselect_all">Deselect all</string> <string name="deselect_all">Deselect all</string>
<string name="enter">ENTER</string>
<string name="welcome_name">File\nRecovery</string>
<string name="welcome_sub_text">Important files shouldn\'t just disappear.</string>
<string name="size_kb">%.2f KB</string> <string name="size_kb">%.2f KB</string>
@ -29,7 +32,7 @@
<string name="audio_title">Audio recovery</string> <string name="audio_title">Audio recovery</string>
<string name="document_title">Document recovery</string> <string name="document_title">Document recovery</string>
<string name="permission_request">Permission is required to access all files</string> <string name="permission_request">Permission is required to access all files</string>
<string name="permission_request_content">File Recovery -All Recovery requires full access to your device storage to search for lost or deleted files.</string> <string name="permission_request_content">File Recovery Tool -All Recovery requires full access to your device storage to search for lost or deleted files.</string>
<string name="permission_request_promote">We will never share, upload, or send your data without your permission.</string> <string name="permission_request_promote">We will never share, upload, or send your data without your permission.</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
@ -88,7 +91,7 @@ wait..</string>
<string name="search">Search</string> <string name="search">Search</string>
<string name="recovered_files">Recovered files</string> <string name="recovered_files">Recovered files</string>
<string name="text_counts">(%d)</string> <string name="text_counts">(%d)</string>
<string name="warning">Please do not uninstall the File Recovery - <string name="warning">Please do not uninstall the File Recovery Tool -
All Recovery to avoid file loss.</string> All Recovery to avoid file loss.</string>
<string name="no_files">No files have been recovered yet~</string> <string name="no_files">No files have been recovered yet~</string>
<string name="scan_photos">Scan photos</string> <string name="scan_photos">Scan photos</string>
@ -96,7 +99,11 @@ All Recovery to avoid file loss.</string>
<string name="scan_audios">Scan audios</string> <string name="scan_audios">Scan audios</string>
<string name="scan_documents">Scan documents</string> <string name="scan_documents">Scan documents</string>
<string name="share_placeholder">Share (%d)</string> <string name="share_placeholder">Share (%d)</string>
<string name="settings">Settings</string>
<string name="share_app">Share App</string>
<string name="share">Share</string>
<string name="privacy_policy">Privacy Policy</string>
<string name="privacy_policy_url">https://file-filerecovery.bitbucket.io//privacy.html</string>
<string-array name="filter_date"> <string-array name="filter_date">

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指向公共根目录 /storage/emulated/0 -->
<external-path
name="scanned_root"
path="." />
<!-- 推荐:用于分享时的临时缓存目录 -->
<external-cache-path
name="share_cache"
path="share/" />
</paths>

View File

@ -2,10 +2,12 @@
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.android) apply false
id("com.google.gms.google-services") version "4.4.2" apply false
id ("com.google.firebase.crashlytics") version "3.0.2" apply false
} }
buildscript { buildscript {
dependencies { dependencies {
classpath("io.objectbox:objectbox-gradle-plugin:4.0.3") classpath("io.objectbox:objectbox-gradle-plugin:5.0.1")
} }
} }

View File

@ -21,3 +21,6 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=2080

6
keystore.properties Normal file
View File

@ -0,0 +1,6 @@
app_name=File Recovery
package_name=com.ux.video.file.filerecovery
keystoreFile=app/FileRecovery
key_alias=FileRecoverykey0
key_store_password=FileRecovery
key_password=FileRecovery