Commit f1cc8b3c authored by jyx's avatar jyx

安装包清理

parent 11202e0a
......@@ -19,7 +19,8 @@
<activity android:name=".ui.albumClean.AlbumCleanActivity" />
<activity
android:name=".ui.multiClean.MultiCleanActivity">
android:name=".ui.multiClean.MultiCleanActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
......
......@@ -11,6 +11,7 @@ import com.mints.cleaner.R
import com.mints.cleaner.model.bean.FileBean
import com.mints.cleaner.util.FormatUtils
import com.mints.cleaner.widget.RoundCheckBox
import com.mints.ktx.ext.dp2px
class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) :
BaseQuickAdapter<FileBean, BaseViewHolder>(layoutResId, data) {
......@@ -25,13 +26,14 @@ class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) :
override fun convert(helper: BaseViewHolder, item: FileBean) {
helper.getView<ImageView>(R.id.iv_item_multi).run {
Glide.with(mContext).load(item.path)
Glide.with(mContext).load(item.image)
.apply(
RequestOptions()
.centerCrop()
.placeholder(drawable)
.override(dp2px(80))
.placeholder(R.mipmap.ic_launcher)
.dontAnimate()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
)
.into(this)
......@@ -40,11 +42,13 @@ class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) :
it.onItemClick(item, helper.adapterPosition)
}
}
}
helper.getView<TextView>(R.id.tv_item_multi_name).text = item.name
helper.getView<TextView>(R.id.tv_item_multi_size).text = FormatUtils.formatFileSize(item.size)
helper.getView<TextView>(R.id.tv_item_multi_time).text = FormatUtils.formatDuration(item.time)
helper.getView<TextView>(R.id.tv_item_multi_time).text = item.time
helper.getView<RoundCheckBox>(R.id.cb_item).run {
setOnCheckedChangeListener(null)
......
package com.mints.cleaner.model.bean
import android.os.Parcel
import android.os.Parcelable
data class FileBean(
val thumbPath: String,
val image: Any,
val path: String,
val time: Long,
val time: String,
val modifyTime: Long,
val name: String,
val size: Long
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString()!!,
parcel.readString()!!,
parcel.readLong(),
parcel.readLong(),
parcel.readString()!!,
parcel.readLong()
)
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.run {
writeString(thumbPath)
writeString(path)
writeLong(time)
writeLong(modifyTime)
writeString(name)
writeLong(size)
}
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<FileBean> {
override fun createFromParcel(parcel: Parcel): FileBean {
return FileBean(parcel)
}
override fun newArray(size: Int): Array<FileBean?> {
return arrayOfNulls(size)
}
}
}
\ No newline at end of file
)
\ No newline at end of file
package com.mints.cleaner.ui.multiClean
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import com.mints.cleaner.R
import com.mints.cleaner.model.bean.FileBean
import com.mints.cleaner.util.FormatUtils
import com.mints.cleaner.util.LogUtil
import java.io.File
object ApkUtils {
/**
* 根据apk存储路径获取apk图标
*/
fun getApkInfo(
context: Context, apkPath: String
): FileBean {
var apkIcon: Drawable = context.getDrawable(R.mipmap.ic_launcher)!!
var apkLabel = ""
val pm: PackageManager = context.packageManager
val info = pm.getPackageArchiveInfo(
apkPath,
PackageManager.GET_ACTIVITIES
)
if (info != null) {
val appInfo = info.applicationInfo
appInfo.sourceDir = apkPath
appInfo.publicSourceDir = apkPath
try {
apkIcon = appInfo.loadIcon(pm)
apkLabel = appInfo.loadLabel(pm).toString()
} catch (e: OutOfMemoryError) {
LogUtil.e(e)
}
}
val apkFile = File(apkPath)
val size = apkFile.length()
val modifiedTime = FormatUtils.formatTime(apkFile.lastModified())
return FileBean(apkIcon, apkPath, modifiedTime, 0L, apkLabel, size)
}
}
\ No newline at end of file
package com.mints.cleaner.ui.multiClean
import android.os.Environment
import java.io.File
import java.util.*
object FileScanModel {
private val mEligibleFiles: ArrayList<String> by lazy { arrayListOf<String>() }
/**
* Java文件操作 根据文件后缀获取全部文件
*/
fun getSuffixFiles(callback: DataCallback, vararg suffixs: String) {
Thread(Runnable {
val sdDir = Environment.getExternalStorageDirectory()
val listFiles = sdDir.listFiles()
getSuffixFile(listFiles, *suffixs)
callback.onSuccess(mEligibleFiles)
}).start()
}
private fun getSuffixFile(files: Array<File>, vararg suffixs: String) {
files?.let {
for (file in files) {
if (file.isDirectory) {
getSuffixFile(file.listFiles(), *suffixs)
} else {
val fileName = file.name
suffixs.forEach { it ->
if (fileName.endsWith(".$it")) {
mEligibleFiles?.let {
it.add(file.absolutePath)
}
}
}
}
}
}
}
interface DataCallback {
fun onSuccess(filePaths: ArrayList<String>?)
}
}
\ No newline at end of file
......@@ -4,6 +4,8 @@ import android.content.Intent
import android.net.Uri
import android.view.View
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.mints.cleaner.R
import com.mints.cleaner.adapter.multiClean.MultiCleanAdapter
import com.mints.cleaner.databinding.ActivityMultiCleanBinding
......@@ -16,7 +18,8 @@ import com.mints.core.base.BaseVMActivity
import kotlinx.android.synthetic.main.activity_album_clean.btn_clean
import kotlinx.android.synthetic.main.activity_multi_clean.*
import java.io.File
import java.util.ArrayList
import java.util.*
class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectListener,
MultiCleanAdapter.OnItemClickListener {
......@@ -34,6 +37,7 @@ class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectList
// 是否全选
private var flag = false
private var sIsScrolling = false
override fun initView() {
binding.run {
......@@ -84,8 +88,25 @@ class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectList
private fun initRecyclerView() {
recyclerview_multi.run {
adapter = multiAdapter
setHasFixedSize(true)
itemAnimator = null
multiAdapter.setOnImageSelectListener(this@MultiCleanActivity)
multiAdapter.setOnItemClickListener1(this@MultiCleanActivity)
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) {
sIsScrolling = true
Glide.with(this@MultiCleanActivity).pauseRequests()
} else if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (sIsScrolling === true) {
Glide.with(this@MultiCleanActivity).resumeRequests()
}
sIsScrolling = false
}
}
})
}
}
......@@ -103,6 +124,50 @@ class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectList
}
})
// MultiCleanModel.loadMusicForSDCard(this, object : MultiCleanModel.DataCallback {
// override fun onSuccess(files: ArrayList<FileBean>?) {
// files?.let {
// dataList.addAll(it)
// }
//
// runOnUiThread {
// multiAdapter.notifyDataSetChanged()
// }
// }
//
// })
// FileScanModel.getSuffixFiles(object : FileScanModel.DataCallback {
// override fun onSuccess(filePaths: ArrayList<String>?) {
// filePaths?.let { it ->
// it.forEach {
// dataList.add(ApkUtils.getApkInfo(this@MultiCleanActivity, it))
// }
//
// dataList.sortWith(Comparator { file, t1 ->
// when {
// file.size < t1.size -> {
// 1
// }
// file.size > t1.size -> {
// -1
// }
// else -> {
// 0
// }
// }
// })
// }
//
// runOnUiThread {
// multiAdapter.notifyDataSetChanged()
// }
//
// }
// }, "apk")
}
override fun startObserve() {
......
......@@ -6,26 +6,31 @@ import android.content.pm.PackageManager
import android.database.ContentObserver
import android.database.Cursor
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import androidx.core.content.ContextCompat
import com.mints.cleaner.model.bean.FileBean
import com.mints.cleaner.ui.albumClean.AlbumCleanModel
import com.mints.cleaner.util.FormatUtils
import com.mints.cleaner.util.UriUtils
import java.io.File
import java.lang.ref.WeakReference
import java.util.*
import kotlin.Comparator
import kotlin.collections.ArrayList
object MultiCleanModel {
/**
* 缓存图片
* 缓存文件
*/
private var cacheImageList: ArrayList<FileBean>? = null
private var isNeedCache = false
private var observer: PhotoContentObserver? = null
/**
* 预加载图片
* 预加载文件
*
* @param context
*/
......@@ -72,7 +77,20 @@ object MultiCleanModel {
}
/**
* 从SDCard加载图片
* 从SDCard加载音乐
*
* @param context
* @param callback
*/
fun loadMusicForSDCard(
context: Context,
callback: DataCallback?
) {
loadMusicForSDCard(context, false, callback)
}
/**
* 从SDCard加载视频
*
* @param context
* @param callback
......@@ -85,7 +103,120 @@ object MultiCleanModel {
}
/**
* 从SDCard加载图片
* 从SDCard加载音乐
*
* @param context
* @param isPreload 是否是预加载
* @param callback
*/
private fun loadMusicForSDCard(
context: Context,
isPreload: Boolean,
callback: DataCallback?
) {
val weakContext: Context? = WeakReference<Context>(context).get()
//由于扫描图片是耗时的操作,所以要在子线程处理。
Thread(Runnable {
var files: ArrayList<FileBean>?
if (cacheImageList == null || isPreload) {
val imageList: ArrayList<FileBean> = loadMusic(weakContext!!)
imageList.sortWith(Comparator { image, t1 ->
when {
image.time > t1.time -> {
1
}
image.time < t1.time -> {
-1
}
else -> {
0
}
}
})
files = imageList
if (isNeedCache) {
cacheImageList = imageList
}
} else {
files = cacheImageList
}
callback?.onSuccess(files)
}).start()
}
/**
* 从SDCard加载视频
*
* @param context
* @return
*/
private fun loadMusic(context: Context): ArrayList<FileBean> {
// 扫描音乐
val mAudioUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val proj = arrayOf(
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATE_MODIFIED,
MediaStore.Audio.Media.SIZE
)
val mContentResolver = context.contentResolver
val mCursor: Cursor? = mContentResolver.query(
mAudioUri,
proj,
null,
null,
MediaStore.Audio.Media.DATE_MODIFIED + " DESC"
)
val files: ArrayList<FileBean> = arrayListOf()
mCursor?.run {
while (moveToNext()) {
// 获取音乐的路径
val id = getInt(getColumnIndex(MediaStore.Audio.Media._ID))
val path = getString(getColumnIndex(MediaStore.Audio.Media.DATA))
// 获取音乐的时间长短
val duration = getInt(getColumnIndex(MediaStore.Audio.Media.DURATION))
// 获取视频的大小
var size = getLong(getColumnIndex(MediaStore.Audio.Media.SIZE))
if (size < 0) {
//某些设备获取size<0,直接计算
size = File(path).length()
}
// 获取视频的名称
val displayName = getString(getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
// 获取视频的修改时间
val modifyTime = getLong(getColumnIndex(MediaStore.Audio.Media.DATE_MODIFIED))
var thumbPath = ""
val formatDuration = FormatUtils.formatDuration(duration.toLong())
files.add(
FileBean(
thumbPath,
path,
formatDuration,
modifyTime,
displayName,
size
)
)
}
close()
}
return files
}
/**
* 从SDCard加载视频
*
* @param context
* @param isPreload 是否是预加载
......@@ -96,44 +227,44 @@ object MultiCleanModel {
isPreload: Boolean,
callback: DataCallback?
) {
val weakContext: Context? = WeakReference<Context>(context).get()
//由于扫描图片是耗时的操作,所以要在子线程处理。
Thread(Runnable {
synchronized(AlbumCleanModel::class.java) {
var files: ArrayList<FileBean>?
if (cacheImageList == null || isPreload) {
val imageList: ArrayList<FileBean> = loadVideo(context)
imageList.sortWith(Comparator { image, t1 ->
when {
image.time > t1.time -> {
1
}
image.time < t1.time -> {
-1
}
else -> {
0
}
var files: ArrayList<FileBean>?
if (cacheImageList == null || isPreload) {
val imageList: ArrayList<FileBean> = loadVideo(weakContext!!)
imageList.sortWith(Comparator { image, t1 ->
when {
image.time > t1.time -> {
1
}
image.time < t1.time -> {
-1
}
else -> {
0
}
})
files = imageList
if (isNeedCache) {
cacheImageList = imageList
}
} else {
files = cacheImageList
})
files = imageList
if (isNeedCache) {
cacheImageList = imageList
}
callback?.onSuccess(files)
} else {
files = cacheImageList
}
callback?.onSuccess(files)
}).start()
}
/**
* 从SDCard加载图片
* 从SDCard加载视频
*
* @param context
* @return
*/
@Synchronized
private fun loadVideo(context: Context): ArrayList<FileBean> {
// 扫描视频
......@@ -204,10 +335,13 @@ object MultiCleanModel {
close()
}
val formatDuration = FormatUtils.formatDuration(duration.toLong())
files.add(
FileBean(
thumbPath, path,
duration.toLong(),
thumbPath,
path,
formatDuration,
modifyTime,
displayName,
size
......@@ -230,16 +364,6 @@ object MultiCleanModel {
return File(filePath).exists()
}
private fun getPathForAndroidQ(
context: Context,
id: Long
): String? {
return UriUtils.getPathForUri(
context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
.appendPath(id.toString()).build()
)
}
/**
* Java文件操作 获取文件扩展名
*/
......@@ -253,23 +377,6 @@ object MultiCleanModel {
return ""
}
/**
* 根据图片路径,获取图片文件夹名称
*
* @param path
* @return
*/
private fun getFolderName(path: String): String {
if (path.isNotEmpty()) {
val strings =
path.split(File.separator.toRegex()).toTypedArray()
if (strings.size >= 2) {
return strings[strings.size - 2]
}
}
return ""
}
interface DataCallback {
fun onSuccess(files: ArrayList<FileBean>?)
}
......
package com.mints.cleaner.util
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import kotlin.math.roundToLong
object FormatUtils {
......@@ -53,4 +54,9 @@ object FormatUtils {
time += second
return time!!
}
fun formatTime(time: Long): String {
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
return formatter.format(time)
}
}
\ No newline at end of file
......@@ -22,6 +22,7 @@ class CleanLoadingView : LinearLayout {
// 时间单位 1000=1秒
private val TIME_INTERVAL: Long = 1000
// 默认展示时间1.5秒
private val DEFAULT_LOAD_TIME: Long = 1500
......@@ -118,11 +119,13 @@ class CleanLoadingView : LinearLayout {
if (animator == null) {
animator = ValueAnimator.ofInt(0, 101)
}
animator?.addUpdateListener { valueAnimator ->
tvLoadingProgress?.text = valueAnimator.getAnimatedValue().toString() + "%"
tvLoadingProgress?.text = valueAnimator.animatedValue.toString() + "%"
// 101目的->防止隐藏时显示进度为99
if (valueAnimator.getAnimatedValue() == 101) {
if (valueAnimator.animatedValue == 101) {
hideLoading()
}
}
......
......@@ -16,16 +16,16 @@
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginTop="12dp"
android:layout_toEndOf="@id/iv_item_multi"
android:ellipsize="middle"
android:singleLine="true"
android:focusable="true"
android:marqueeRepeatLimit="marquee_forever"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="18sp" />
android:textSize="20sp" />
<TextView
android:id="@+id/tv_item_multi_size"
......@@ -36,7 +36,7 @@
android:layout_marginTop="10dp"
android:layout_toEndOf="@id/iv_item_multi"
android:textColor="@color/gray"
android:textSize="18sp" />
android:textSize="14sp" />
<TextView
android:id="@+id/tv_item_multi_time"
......@@ -47,7 +47,7 @@
android:layout_marginTop="10dp"
android:layout_toEndOf="@id/tv_item_multi_size"
android:textColor="@color/gray"
android:textSize="18sp" />
android:textSize="14sp" />
<com.mints.cleaner.widget.RoundCheckBox
android:id="@+id/cb_item"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment