Commit 45e22624 authored by wanglei's avatar wanglei

[新增]相识图片

parent 97113490
......@@ -78,6 +78,11 @@
android:exported="false"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".ui.similar.SimilarPhotosActivity"
android:exported="false"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<provider
android:name="androidx.core.content.FileProvider"
......
{"v":"5.6.3","fr":25,"ip":0,"op":36,"w":870,"h":870,"nm":"1-扫描动画","ddd":0,"assets":[{"id":"image_0","w":120,"h":120,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":198,"h":198,"u":"images/","p":"img_1.png","e":0},{"id":"image_2","w":678,"h":678,"u":"images/","p":"img_2.png","e":0},{"id":"image_3","w":678,"h":678,"u":"images/","p":"img_3.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[60],"e":[0]},{"t":40}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[141,893,0],"e":[437,429,0],"to":[0,0,0],"ti":[0,0,0]},{"t":35}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[892,226,0],"e":[462,460,0],"to":[0,0,0],"ti":[0,0,0]},{"t":20}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":5,"s":[60],"e":[0]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":5,"s":[-81,761,0],"e":[431,435,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[22,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":10,"s":[60],"e":[0]},{"t":30}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":10,"s":[574,-48,0],"e":[434,408,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":0,"s":[60],"e":[0]},{"t":20}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":0,"s":[-48,-40,0],"e":[444,444,0],"to":[0,0,0],"ti":[0,0,0]},{"t":18}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[36,36],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"小飞圆","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.2],"y":[0]},"t":16,"s":[60],"e":[0]},{"t":36}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":16,"s":[1018,824,0],"e":[446,462,0],"to":[0,0,0],"ti":[0,0,0]},{"t":31}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[26,26],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"小飞圆","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":2,"nm":"icon_clean_home","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[60,60,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":2,"nm":"小圆","refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[99,99,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":2,"nm":"转圈园","refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0],"e":[360]},{"t":35}],"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":2,"nm":"背景圆","refId":"image_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[435,435,0],"ix":2},"a":{"a":0,"k":[339,339,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":75,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
......@@ -33,6 +33,7 @@ import com.base.appzxhy.bean.FeatureBean.Companion.BATTERY_INFO
import com.base.appzxhy.bean.FeatureBean.Companion.JUNK_CLEAN
import com.base.appzxhy.bean.FeatureBean.Companion.LARGE_FILE_CLEAN
import com.base.appzxhy.bean.FeatureBean.Companion.SCREENSHOT_CLEAN
import com.base.appzxhy.bean.FeatureBean.Companion.SIMILAR_PHOTOS
import com.base.appzxhy.bean.FileBean
import com.base.appzxhy.business.ads.AdsMgr
import com.base.appzxhy.business.ads.AdsShowCallBack
......@@ -43,6 +44,7 @@ import com.base.appzxhy.ui.clean.JunkCleanActivity
import com.base.appzxhy.ui.largefile.LargeFileCleanActivity
import com.base.appzxhy.ui.main.MainActivity
import com.base.appzxhy.ui.screenshot.ScreenshotCleanActivity
import com.base.appzxhy.ui.similar.SimilarPhotosActivity
import com.base.appzxhy.utils.ActivityLauncher
import com.base.appzxhy.utils.ActivityManagerUtils
import com.base.appzxhy.utils.FileUtils
......@@ -279,7 +281,7 @@ abstract class BaseActivity<VB : ViewBinding>(
@SuppressLint("SetTextI18n")
open fun showAdAnimation(
lottieConfig: LottieConfig,
lottieConfig: LottieEnum,
finish: (() -> Unit)? = null
) {
......@@ -310,7 +312,7 @@ abstract class BaseActivity<VB : ViewBinding>(
when (lottieConfig) {
LottieConfig.CLEAN_LOTTIE -> {
LottieEnum.CLEAN_LOTTIE -> {
lottie?.addAnimatorUpdateListener { animation ->
val value = animation.animatedValue as Float * 100
tvProgress?.text = "${value.toInt()}%"
......@@ -377,18 +379,23 @@ fun Activity.jumpAction(key: String) {
goToAc(ScreenshotCleanActivity::class.java)
}
SIMILAR_PHOTOS -> {
goToAc(SimilarPhotosActivity::class.java)
}
}
}
enum class LottieConfig(val data: String, val images: String? = null, val isLoop: Boolean = true) {
enum class LottieEnum(val data: String, val images: String? = null, val isLoop: Boolean = true) {
BATTERY_INFO_LOTTIE("battery_info/scan/data.json", "battery_info/scan/images", true),
CLEAN_LOTTIE("clean.json"),
LARGE_FILE_LOTTIE("large_files/scan/data.json", "large_files/scan/images"),
SCREENSHOT_LOTTIE("screenshot/scan/data.json", "screenshot/scan/images"),
SIMILAR_PHOTOS_LOTTIE("similar_photos/scan/data.json", "similar_photos/scan/images"),
// PHOTO_COMPRESSION_LOTTIE("photo_compression/scan/data.json", "photo_compression/scan/images"),
// SIMILAR_PHOTOS("similar_photos/scan/data.json", "similar_photos/scan/images"),
// WHATSAPP("whatsapp/scan/data.json", "whatsapp/scan/images"),
// WEATHER(""),
// CONSTELLATION("")
......
package com.base.appzxhy.bean
data class SimilarBean(
val url: String,
val hashCode: String,
val avgPixel: Int,
var size: Long,
var isSelect: Boolean,
var items: MutableList<SimilarBean> = mutableListOf()
)
\ No newline at end of file
......@@ -5,7 +5,7 @@ import android.content.IntentFilter
import androidx.recyclerview.widget.ConcatAdapter
import com.base.appzxhy.R
import com.base.appzxhy.base.BaseActivity
import com.base.appzxhy.base.LottieConfig
import com.base.appzxhy.base.LottieEnum
import com.base.appzxhy.databinding.ActivityBatteryInfo2Binding
import com.base.appzxhy.ui.adapter.AdAdapter
import com.base.appzxhy.utils.Utils
......@@ -34,7 +34,7 @@ class BatteryInfoActivity : BaseActivity<ActivityBatteryInfo2Binding>(ActivityBa
}
override fun initView() {
showAdAnimation(LottieConfig.BATTERY_INFO_LOTTIE) {
showAdAnimation(LottieEnum.BATTERY_INFO_LOTTIE) {
adAdapter.submitList(listOf(""))
}
batteryReceiver = BatteryReceiver { updateBatteryInfo() }
......
......@@ -12,8 +12,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.base.appzxhy.R
import com.base.appzxhy.base.BaseActivity
import com.base.appzxhy.base.LottieConfig
import com.base.appzxhy.base.cleanFiles
import com.base.appzxhy.base.LottieEnum
import com.base.appzxhy.bean.FeatureBean.Companion.JUNK_CLEAN
import com.base.appzxhy.bean.FileBean
import com.base.appzxhy.business.ads.AdsMgr
......@@ -170,7 +169,7 @@ class JunkCleanActivity : BaseActivity<ActivityJunkCleanBinding>(ActivityJunkCle
if (isBackDisable) return@setOnClickListener
if (total > 0) {
showAdAnimation(LottieConfig.CLEAN_LOTTIE) {
showAdAnimation(LottieEnum.CLEAN_LOTTIE) {
// cleanFiles(selectedFiles)
CleanResultActivity.functionKey = JUNK_CLEAN
......
......@@ -15,7 +15,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.base.appzxhy.R
import com.base.appzxhy.base.BaseActivity
import com.base.appzxhy.base.LottieConfig
import com.base.appzxhy.base.LottieEnum
import com.base.appzxhy.base.cleanFiles
import com.base.appzxhy.bean.FeatureBean.Companion.LARGE_FILE_CLEAN
import com.base.appzxhy.bean.FileBean
......@@ -337,7 +337,7 @@ class LargeFileCleanActivity : BaseActivity<ActivityLargeFileCleanBinding>(Activ
binding.rvLargeFile.adapter = adapter
binding.rvLargeFile.layoutManager = LinearLayoutManager(this)
initData()
showAdAnimation(LottieConfig.LARGE_FILE_LOTTIE) {
showAdAnimation(LottieEnum.LARGE_FILE_LOTTIE) {
AdsMgr.showNative(binding.flAd, R.layout.layout_admob_native_custom)
}
}
......
......@@ -7,12 +7,11 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.setPadding
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.base.appzxhy.R
import com.base.appzxhy.base.BaseActivity
import com.base.appzxhy.base.LottieConfig
import com.base.appzxhy.base.LottieEnum
import com.base.appzxhy.base.cleanFiles
import com.base.appzxhy.bean.FeatureBean.Companion.SCREENSHOT_CLEAN
import com.base.appzxhy.bean.FileBean
......@@ -157,7 +156,7 @@ class ScreenshotCleanActivity : BaseActivity<ActivityScreenshotCleanBinding>(Act
binding.expandableFiles.layoutManager = gridLayoutManager
initData()
showAdAnimation(LottieConfig.SCREENSHOT_LOTTIE) {
showAdAnimation(LottieEnum.SCREENSHOT_LOTTIE) {
AdsMgr.showNative(binding.flAd, R.layout.layout_admob_native_custom)
}
}
......@@ -181,6 +180,7 @@ class ScreenshotCleanActivity : BaseActivity<ActivityScreenshotCleanBinding>(Act
}
}
@SuppressLint("NotifyDataSetChanged")
override fun initListener() {
super.initListener()
binding.ivBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
......
package com.base.appzxhy.ui.similar
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.airbnb.lottie.LottieAnimationView
import com.base.appzxhy.R
import com.base.appzxhy.base.BaseActivity
import com.base.appzxhy.base.LottieEnum
import com.base.appzxhy.bean.FeatureBean.Companion.SIMILAR_PHOTOS
import com.base.appzxhy.bean.SimilarBean
import com.base.appzxhy.business.ads.AdsMgr
import com.base.appzxhy.business.ads.AdsShowCallBack
import com.base.appzxhy.databinding.ActivitySimilarPhotosBinding
import com.base.appzxhy.databinding.ItemSimilarPhotosBinding
import com.base.appzxhy.databinding.ItemSimilarPhotosChildBinding
import com.base.appzxhy.ui.cleanresult.CleanResultActivity
import com.base.appzxhy.ui.dialog.ConfirmDialog
import com.base.appzxhy.utils.MediaUtils
import com.base.appzxhy.utils.Utils
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.util.concurrent.Semaphore
class SimilarPhotosActivity : BaseActivity<ActivitySimilarPhotosBinding>(ActivitySimilarPhotosBinding::inflate) {
private val semaphore = Semaphore(20)
private var state = 0
private var list = mutableListOf<SimilarBean>()
private var selectedFiles = listOf<SimilarBean>()
private var total: Long = 0
private val adapter by lazy {
class ViewHolder(val binding: ItemSimilarPhotosBinding) : RecyclerView.ViewHolder(binding.root)
object : RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemSimilarPhotosBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = list[position]
val total = item.items.filter { it.isSelect }.sumOf { it.size }
holder.binding.tvSize.text = Utils.getSizeFormat(total)
holder.binding.ivSelect.isSelected = item.items[0].isSelect
val width = resources.displayMetrics.widthPixels
val margin = resources.getDimensionPixelSize(R.dimen.dp_45)
val imageWidth = (width - margin) / 4
val largePhotoWidth = imageWidth * 2
val largePhotoHeight = (largePhotoWidth * 0.92).toInt()
holder.binding.ivFirstPhoto.layoutParams.width = largePhotoWidth
holder.binding.ivFirstPhoto.layoutParams.height = largePhotoHeight
val round = resources.getDimensionPixelSize(R.dimen.dp_8)
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.items[0].url)
.override(largePhotoWidth, largePhotoHeight)
.apply(options)
.into(holder.binding.ivFirstPhoto)
holder.binding.ivFirstPhoto.setOnClickListener {
item.items[0].isSelect = !item.items[0].isSelect
holder.binding.ivSelect.isSelected = item.items[0].isSelect
updateView(holder, item)
}
val largePhotos = item.items.take(5)
val largeAdapter = SimilarPhotosChildAdapter(largePhotos.subList(1, largePhotos.size)) {
updateView(holder, item)
}
holder.binding.rvLargePhoto.layoutManager =
GridLayoutManager(this@SimilarPhotosActivity, 2, GridLayoutManager.VERTICAL, false)
holder.binding.rvLargePhoto.adapter = largeAdapter
val photos = if (item.items.size > 4) item.items.subList(5, item.items.size) else mutableListOf()
val adapter = SimilarPhotosChildAdapter(photos) { updateView(holder, item) }
holder.binding.rvPhoto.layoutManager =
GridLayoutManager(this@SimilarPhotosActivity, 4, GridLayoutManager.VERTICAL, false)
holder.binding.rvPhoto.adapter = adapter
}
private fun updateView(holder: ViewHolder, item: SimilarBean) {
val total = item.items.filter { it.isSelect }.sumOf { it.size }
holder.binding.tvSize.text = Utils.getSizeFormat(total)
updateSelection()
}
}
}
inner class SimilarPhotosChildAdapter(
var list: List<SimilarBean>,
private val block: () -> Unit
) : RecyclerView.Adapter<SimilarPhotosChildAdapter.ViewHolder>() {
inner class ViewHolder(val binding: ItemSimilarPhotosChildBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
val binding = ItemSimilarPhotosChildBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = list[position]
val width = holder.itemView.context.resources.displayMetrics.widthPixels
val context = holder.itemView.context
val margin = context.resources.getDimensionPixelSize(R.dimen.dp_45)
val imageWidth = (width - margin) / 4
val imageHeight = (imageWidth * 0.91).toInt()
holder.binding.ivPhoto.layoutParams.width = imageWidth
holder.binding.ivPhoto.layoutParams.height = imageHeight
holder.binding.ivSelect.isSelected = item.isSelect
holder.itemView.setOnClickListener {
item.isSelect = !item.isSelect
notifyItemChanged(position)
block.invoke()
updateSelection()
}
val round = context.resources.getDimensionPixelSize(R.dimen.dp_8)
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.url)
.override(imageWidth, imageHeight)
.apply(options)
.into(holder.binding.ivPhoto)
}
}
private fun updateSelection() {
selectedFiles = list.flatMap { it.items.takeIf { it.isNotEmpty() }?.filter { it.isSelect } ?: emptyList() }
total = selectedFiles.sumOf { it.size }
showSelection(total > 0)
}
private fun showSelection(isSelection: Boolean) {
binding.tvDelete.isEnabled = isSelection
setTextSize(binding.tvDelete, getString(R.string.delete), total)
}
fun setTextSize(view: TextView, content: String, total: Long) {
view.text = if (total > 0) "$content (${Utils.getSizeFormat(total)})" else content
}
override fun useDefaultImmersive() {
setStatusBarColor()
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, 0, systemBars.right, systemBars.bottom)
binding.clTop.setPadding(0, systemBars.top, 0, 0)
insets
}
}
override fun initView() {
super.initView()
binding.rvPhoto.adapter = adapter
binding.rvPhoto.layoutManager = LinearLayoutManager(this)
initData()
showAdAnimation(LottieEnum.SIMILAR_PHOTOS_LOTTIE) {
if (state != 0) scanComplete()
state = 1
AdsMgr.showNative(binding.flAd, R.layout.layout_admob_native_custom)
}
}
private fun initData() {
lifecycleScope.launch(Dispatchers.IO) {
val images = MediaUtils.findImageFiles(this@SimilarPhotosActivity)
val similarBeans = images.mapNotNull { value ->
lifecycleScope.async(Dispatchers.IO) {
semaphore.acquire() // 请求许可
try {
SimilarUtils.createImage(value.path)
} finally {
semaphore.release() // 释放许可
}
}.await()
}
val hasSame = mutableSetOf<String>()
similarBeans.forEach { similar ->
if (hasSame.contains(similar.url)) return@forEach
hasSame.add(similar.url)
similar.items.add(similar)
similarBeans.forEach {
if (!hasSame.contains(it.url) && SimilarUtils.similarCondition(similar, it)) {
hasSame.add(it.url)
similar.items.add(it)
}
}
}
list = similarBeans.filter { it.items.size > 1 }.toMutableList()
launch(Dispatchers.Main) {
if (state != 0) scanComplete()
state = 2
}
}
}
private fun scanComplete() {
binding.root.postDelayed({
setSelectAll(true)
stopAnimation()
}, 2500)
}
fun stopAnimation() {
val lottie: LottieAnimationView? = findViewById(R.id.lottie_animation)
lottie?.clearAnimation()
findViewById<View?>(R.id.layout_animation)?.visibility = View.GONE
isBackDisable = false
}
override fun initListener() {
super.initListener()
binding.ivBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
binding.ivSelect.setOnClickListener {
it.isSelected = !it.isSelected
setSelectAll(it.isSelected)
}
binding.tvDelete.setOnClickListener {
if (selectedFiles.isEmpty()) return@setOnClickListener
val dialog = ConfirmDialog(this)
dialog.action = {
AdsMgr.showInsert(this, showCallBack = object : AdsShowCallBack() {
override fun next() {
CleanResultActivity.total = total
CleanResultActivity.functionKey = SIMILAR_PHOTOS
CleanResultActivity.titleName = getString(R.string.similar_photos)
goToAc(CleanResultActivity::class.java)
}
})
}
dialog.showDialog("delete")
}
}
@SuppressLint("NotifyDataSetChanged")
private fun setSelectAll(isSelected: Boolean) {
binding.ivSelect.isSelected = isSelected
list.forEach { similar ->
similar.items.forEachIndexed { index, similarBean ->
similarBean.isSelect = isSelected
if (isSelected && index == 0) {
similarBean.isSelect = false
}
}
}
adapter.notifyDataSetChanged()
binding.ivSelect.setImageResource(if (isSelected) R.drawable.icon else R.drawable.icon_n)
updateSelection()
}
}
\ No newline at end of file
package com.base.appzxhy.ui.similar
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.ThumbnailUtils
import com.base.appzxhy.bean.SimilarBean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.math.pow
object SimilarUtils {
private fun loadBitmapFromFile(path: String): Bitmap? {
val bitmap = BitmapFactory.decodeFile(path)
if (bitmap.width <= 0 || bitmap.height <= 0) {
return null
}
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, options)
if (options.outHeight == -1 || options.outWidth == -1) {
return null
}
var inSampleSize =
(0.5f + options.outHeight.toFloat() / bitmap.height.toFloat()).coerceAtLeast(0.5f + options.outWidth.toFloat() / bitmap.width.toFloat())
.toInt()
inSampleSize += 1
options.inSampleSize = inSampleSize.coerceAtLeast(1)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, options)
}
suspend fun createImage(path: String): SimilarBean? {
return withContext(Dispatchers.IO) {
try {
val width = 8
val height = 8
val source = loadBitmapFromFile(path)
val thumb = ThumbnailUtils.extractThumbnail(source, width, height)
val pixels = IntArray(width * height)
for (i in 0 until width) {
for (j in 0 until height) {
pixels[i * height + j] = rgbToGray(thumb.getPixel(i, j))
}
}
val avgPixel = average(pixels)
val comps = IntArray(width * height)
for (i in comps.indices) {
if (pixels[i] >= avgPixel) {
comps[i] = 1
} else {
comps[i] = 0
}
}
val hashCode = StringBuffer()
var i = 0
while (i < comps.size) {
val result = comps[i] * 2.0.pow(3.0).toInt() + (comps[i + 1] * 2.0.pow(2.0)
.toInt()) + (comps[i + 2] * 2.0.pow(1.0).toInt()) + comps[i + 3]
hashCode.append(binaryToHex(result))
i += 4
}
recycleBitmap(thumb)
recycleBitmap(source)
return@withContext SimilarBean(
path,
hashCode.toString(),
avgPixel,
File(path).length(),
false
)
} catch (_: Exception) {
return@withContext null
}
}
}
private fun rgbToGray(pixels: Int): Int {
val red = Color.red(pixels)
val green = Color.green(pixels)
val blue = Color.blue(pixels)
return (0.3 * red + 0.59 * green + 0.11 * blue).toInt()
}
private fun average(pixels: IntArray): Int {
return (pixels.sumOf { it }.toFloat() / pixels.size).toInt()
}
private fun recycleBitmap(thumb: Bitmap?) {
if (thumb?.isRecycled == false) {
thumb.recycle()
}
}
fun similarCondition(first: SimilarBean, second: SimilarBean): Boolean {
return hammingDistance(
first.hashCode,
second.hashCode
) <= 6 && (first.avgPixel.toFloat() / second.avgPixel) in 0.8..1.0
}
private fun hammingDistance(sourceHashCode: String, hashCode: String): Int {
var difference = 0
for (i in sourceHashCode.indices) {
if (sourceHashCode[i] != hashCode[i]) {
difference++
}
}
return difference
}
private fun binaryToHex(binary: Int) = when (binary) {
0 -> '0'
1 -> '1'
2 -> '2'
3 -> '3'
4 -> '4'
5 -> '5'
6 -> '6'
7 -> '7'
8 -> '8'
9 -> '9'
10 -> 'a'
11 -> 'b'
12 -> 'c'
13 -> 'd'
14 -> 'e'
15 -> 'f'
else -> ' '
}
}
\ No newline at end of file
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_f7fafa">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingVertical="6dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivBack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_8"
android:padding="@dimen/dp_8"
android:src="@drawable/icon_return_w" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/similar_photos"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.base.appzxhy.business.ads.NativeParentView
android:id="@+id/flAd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="@id/cl_top">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/zhanwei1"
tools:ignore="ContentDescription" />
</com.base.appzxhy.business.ads.NativeParentView>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@color/white"
android:paddingVertical="13dp"
android:paddingStart="15dp"
android:paddingEnd="24dp"
app:layout_constraintTop_toBottomOf="@id/flAd">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/auto_select"
android:textColor="@color/black"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_select"
android:layout_width="@dimen/dp_50"
android:layout_height="@dimen/dp_20"
android:src="@drawable/icon_n"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_photo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scrollbars="none"
app:layout_constraintBottom_toTopOf="@id/ll_delete"
app:layout_constraintTop_toBottomOf="@id/cl_select" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingHorizontal="30dp"
android:paddingTop="20dp"
android:paddingBottom="55dp"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="@+id/tv_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_enable_50"
android:gravity="center"
android:paddingVertical="12dp"
android:text="@string/delete"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</androidx.appcompat.widget.LinearLayoutCompat>
<include
android:id="@+id/layout_animation"
layout="@layout/layout_animation"
android:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_occupies"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:text="@string/occupies"
android:textColor="@color/color_9b9595"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:text="0B"
android:textColor="@color/color_9b9595"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="@id/tv_occupies"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/tv_occupies">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_first_photo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginStart="15dp"
android:layout_marginTop="4dp"
android:src="@color/black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="8dp"
android:src="@drawable/bg_select_yuan"
app:layout_constraintEnd_toEndOf="@id/iv_first_photo"
app:layout_constraintTop_toTopOf="@id/iv_first_photo" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_large_photo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="15dp"
android:nestedScrollingEnabled="false"
android:scrollbars="none"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_first_photo"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_photo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:nestedScrollingEnabled="false"
android:paddingHorizontal="15dp"
android:paddingBottom="10dp"
android:scrollbars="none"
app:layout_constraintTop_toBottomOf="@id/cl_large" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?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"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_photo"
android:layout_width="80dp"
android:layout_height="73dp"
android:layout_marginTop="4dp"
android:src="@color/color_9b9595"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="8dp"
android:src="@drawable/bg_select_yuan"
app:layout_constraintEnd_toEndOf="@id/iv_photo"
app:layout_constraintTop_toTopOf="@id/iv_photo" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -94,6 +94,8 @@ Please rest assured that we will handle your information in strict accordance wi
<string name="delete_content">Selected files cannot be recovered after deletion. Continue anyway?</string>
<string name="screenshots_totally">Screenshots totally</string>
<string name="select_all">Select All</string>
<string name="auto_select">Auto Select</string>
<string name="occupies">Occupies</string>
</resources>
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