Commit f1cc8b3c authored by jyx's avatar jyx

安装包清理

parent 11202e0a
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
<activity android:name=".ui.albumClean.AlbumCleanActivity" /> <activity android:name=".ui.albumClean.AlbumCleanActivity" />
<activity <activity
android:name=".ui.multiClean.MultiCleanActivity"> android:name=".ui.multiClean.MultiCleanActivity"
android:launchMode="singleTop">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
......
...@@ -11,6 +11,7 @@ import com.mints.cleaner.R ...@@ -11,6 +11,7 @@ import com.mints.cleaner.R
import com.mints.cleaner.model.bean.FileBean import com.mints.cleaner.model.bean.FileBean
import com.mints.cleaner.util.FormatUtils import com.mints.cleaner.util.FormatUtils
import com.mints.cleaner.widget.RoundCheckBox import com.mints.cleaner.widget.RoundCheckBox
import com.mints.ktx.ext.dp2px
class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) : class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) :
BaseQuickAdapter<FileBean, BaseViewHolder>(layoutResId, data) { BaseQuickAdapter<FileBean, BaseViewHolder>(layoutResId, data) {
...@@ -25,13 +26,14 @@ class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) : ...@@ -25,13 +26,14 @@ class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) :
override fun convert(helper: BaseViewHolder, item: FileBean) { override fun convert(helper: BaseViewHolder, item: FileBean) {
helper.getView<ImageView>(R.id.iv_item_multi).run { helper.getView<ImageView>(R.id.iv_item_multi).run {
Glide.with(mContext).load(item.path) Glide.with(mContext).load(item.image)
.apply( .apply(
RequestOptions() RequestOptions()
.centerCrop() .centerCrop()
.placeholder(drawable) .override(dp2px(80))
.placeholder(R.mipmap.ic_launcher)
.dontAnimate() .dontAnimate()
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
) )
.into(this) .into(this)
...@@ -40,11 +42,13 @@ class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) : ...@@ -40,11 +42,13 @@ class MultiCleanAdapter(layoutResId: Int, data: List<FileBean>) :
it.onItemClick(item, helper.adapterPosition) 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_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_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 { helper.getView<RoundCheckBox>(R.id.cb_item).run {
setOnCheckedChangeListener(null) setOnCheckedChangeListener(null)
......
package com.mints.cleaner.model.bean package com.mints.cleaner.model.bean
import android.os.Parcel
import android.os.Parcelable
data class FileBean( data class FileBean(
val thumbPath: String, val image: Any,
val path: String, val path: String,
val time: Long, val time: String,
val modifyTime: Long, val modifyTime: Long,
val name: String, val name: String,
val size: Long val size: Long
) : Parcelable { )
constructor(parcel: Parcel) : this( \ No newline at end of file
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
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 ...@@ -4,6 +4,8 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.view.View import android.view.View
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.mints.cleaner.R import com.mints.cleaner.R
import com.mints.cleaner.adapter.multiClean.MultiCleanAdapter import com.mints.cleaner.adapter.multiClean.MultiCleanAdapter
import com.mints.cleaner.databinding.ActivityMultiCleanBinding import com.mints.cleaner.databinding.ActivityMultiCleanBinding
...@@ -16,7 +18,8 @@ import com.mints.core.base.BaseVMActivity ...@@ -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_album_clean.btn_clean
import kotlinx.android.synthetic.main.activity_multi_clean.* import kotlinx.android.synthetic.main.activity_multi_clean.*
import java.io.File import java.io.File
import java.util.ArrayList import java.util.*
class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectListener, class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectListener,
MultiCleanAdapter.OnItemClickListener { MultiCleanAdapter.OnItemClickListener {
...@@ -34,6 +37,7 @@ class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectList ...@@ -34,6 +37,7 @@ class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectList
// 是否全选 // 是否全选
private var flag = false private var flag = false
private var sIsScrolling = false
override fun initView() { override fun initView() {
binding.run { binding.run {
...@@ -84,8 +88,25 @@ class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectList ...@@ -84,8 +88,25 @@ class MultiCleanActivity : BaseVMActivity(), MultiCleanAdapter.OnImageSelectList
private fun initRecyclerView() { private fun initRecyclerView() {
recyclerview_multi.run { recyclerview_multi.run {
adapter = multiAdapter adapter = multiAdapter
setHasFixedSize(true)
itemAnimator = null
multiAdapter.setOnImageSelectListener(this@MultiCleanActivity) multiAdapter.setOnImageSelectListener(this@MultiCleanActivity)
multiAdapter.setOnItemClickListener1(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 ...@@ -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() { override fun startObserve() {
......
...@@ -6,26 +6,31 @@ import android.content.pm.PackageManager ...@@ -6,26 +6,31 @@ import android.content.pm.PackageManager
import android.database.ContentObserver import android.database.ContentObserver
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.os.Environment
import android.provider.MediaStore import android.provider.MediaStore
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.mints.cleaner.model.bean.FileBean import com.mints.cleaner.model.bean.FileBean
import com.mints.cleaner.ui.albumClean.AlbumCleanModel import com.mints.cleaner.ui.albumClean.AlbumCleanModel
import com.mints.cleaner.util.FormatUtils
import com.mints.cleaner.util.UriUtils import com.mints.cleaner.util.UriUtils
import java.io.File import java.io.File
import java.lang.ref.WeakReference
import java.util.* import java.util.*
import kotlin.Comparator import kotlin.Comparator
import kotlin.collections.ArrayList
object MultiCleanModel { object MultiCleanModel {
/** /**
* 缓存图片 * 缓存文件
*/ */
private var cacheImageList: ArrayList<FileBean>? = null private var cacheImageList: ArrayList<FileBean>? = null
private var isNeedCache = false private var isNeedCache = false
private var observer: PhotoContentObserver? = null private var observer: PhotoContentObserver? = null
/** /**
* 预加载图片 * 预加载文件
* *
* @param context * @param context
*/ */
...@@ -72,7 +77,20 @@ object MultiCleanModel { ...@@ -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 context
* @param callback * @param callback
...@@ -85,7 +103,120 @@ object MultiCleanModel { ...@@ -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 context
* @param isPreload 是否是预加载 * @param isPreload 是否是预加载
...@@ -96,44 +227,44 @@ object MultiCleanModel { ...@@ -96,44 +227,44 @@ object MultiCleanModel {
isPreload: Boolean, isPreload: Boolean,
callback: DataCallback? callback: DataCallback?
) { ) {
val weakContext: Context? = WeakReference<Context>(context).get()
//由于扫描图片是耗时的操作,所以要在子线程处理。 //由于扫描图片是耗时的操作,所以要在子线程处理。
Thread(Runnable { Thread(Runnable {
synchronized(AlbumCleanModel::class.java) { var files: ArrayList<FileBean>?
var files: ArrayList<FileBean>? if (cacheImageList == null || isPreload) {
if (cacheImageList == null || isPreload) { val imageList: ArrayList<FileBean> = loadVideo(weakContext!!)
val imageList: ArrayList<FileBean> = loadVideo(context) imageList.sortWith(Comparator { image, t1 ->
imageList.sortWith(Comparator { image, t1 -> when {
when { image.time > t1.time -> {
image.time > t1.time -> { 1
1 }
} image.time < t1.time -> {
image.time < t1.time -> { -1
-1 }
} else -> {
else -> { 0
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() }).start()
} }
/** /**
* 从SDCard加载图片 * 从SDCard加载视频
* *
* @param context * @param context
* @return * @return
*/ */
@Synchronized
private fun loadVideo(context: Context): ArrayList<FileBean> { private fun loadVideo(context: Context): ArrayList<FileBean> {
// 扫描视频 // 扫描视频
...@@ -204,10 +335,13 @@ object MultiCleanModel { ...@@ -204,10 +335,13 @@ object MultiCleanModel {
close() close()
} }
val formatDuration = FormatUtils.formatDuration(duration.toLong())
files.add( files.add(
FileBean( FileBean(
thumbPath, path, thumbPath,
duration.toLong(), path,
formatDuration,
modifyTime, modifyTime,
displayName, displayName,
size size
...@@ -230,16 +364,6 @@ object MultiCleanModel { ...@@ -230,16 +364,6 @@ object MultiCleanModel {
return File(filePath).exists() 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文件操作 获取文件扩展名 * Java文件操作 获取文件扩展名
*/ */
...@@ -253,23 +377,6 @@ object MultiCleanModel { ...@@ -253,23 +377,6 @@ object MultiCleanModel {
return "" 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 { interface DataCallback {
fun onSuccess(files: ArrayList<FileBean>?) fun onSuccess(files: ArrayList<FileBean>?)
} }
......
package com.mints.cleaner.util package com.mints.cleaner.util
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.SimpleDateFormat
import kotlin.math.roundToLong import kotlin.math.roundToLong
object FormatUtils { object FormatUtils {
...@@ -53,4 +54,9 @@ object FormatUtils { ...@@ -53,4 +54,9 @@ object FormatUtils {
time += second time += second
return time!! 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 { ...@@ -22,6 +22,7 @@ class CleanLoadingView : LinearLayout {
// 时间单位 1000=1秒 // 时间单位 1000=1秒
private val TIME_INTERVAL: Long = 1000 private val TIME_INTERVAL: Long = 1000
// 默认展示时间1.5秒 // 默认展示时间1.5秒
private val DEFAULT_LOAD_TIME: Long = 1500 private val DEFAULT_LOAD_TIME: Long = 1500
...@@ -118,11 +119,13 @@ class CleanLoadingView : LinearLayout { ...@@ -118,11 +119,13 @@ class CleanLoadingView : LinearLayout {
if (animator == null) { if (animator == null) {
animator = ValueAnimator.ofInt(0, 101) animator = ValueAnimator.ofInt(0, 101)
} }
animator?.addUpdateListener { valueAnimator -> animator?.addUpdateListener { valueAnimator ->
tvLoadingProgress?.text = valueAnimator.getAnimatedValue().toString() + "%" tvLoadingProgress?.text = valueAnimator.animatedValue.toString() + "%"
// 101目的->防止隐藏时显示进度为99 // 101目的->防止隐藏时显示进度为99
if (valueAnimator.getAnimatedValue() == 101) { if (valueAnimator.animatedValue == 101) {
hideLoading() hideLoading()
} }
} }
......
...@@ -16,16 +16,16 @@ ...@@ -16,16 +16,16 @@
android:layout_width="200dp" android:layout_width="200dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginTop="10dp" android:layout_marginTop="12dp"
android:layout_toEndOf="@id/iv_item_multi" android:layout_toEndOf="@id/iv_item_multi"
android:ellipsize="middle" android:ellipsize="middle"
android:singleLine="true"
android:focusable="true" android:focusable="true"
android:marqueeRepeatLimit="marquee_forever"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true" android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="18sp" /> android:textSize="20sp" />
<TextView <TextView
android:id="@+id/tv_item_multi_size" android:id="@+id/tv_item_multi_size"
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_toEndOf="@id/iv_item_multi" android:layout_toEndOf="@id/iv_item_multi"
android:textColor="@color/gray" android:textColor="@color/gray"
android:textSize="18sp" /> android:textSize="14sp" />
<TextView <TextView
android:id="@+id/tv_item_multi_time" android:id="@+id/tv_item_multi_time"
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_toEndOf="@id/tv_item_multi_size" android:layout_toEndOf="@id/tv_item_multi_size"
android:textColor="@color/gray" android:textColor="@color/gray"
android:textSize="18sp" /> android:textSize="14sp" />
<com.mints.cleaner.widget.RoundCheckBox <com.mints.cleaner.widget.RoundCheckBox
android:id="@+id/cb_item" 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