Commit 3ba89098 authored by wanglei's avatar wanglei

Initial commit

parent 7b772992
# smart file manager
文件管理过包,过包后换文件清理
文件管理过包,过包后换文件清理
\ No newline at end of file
# vps
47.243.82.221:11969
admin_ldzb6s
xSKfayzu1O
```json
{
"project_info": {
"project_number": "999706837181",
"project_id": "super-file-manager-fb486",
"storage_bucket": "super-file-manager-fb486.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:999706837181:android:bc4406613dbce941e7f7d4",
"android_client_info": {
"package_name": "com.kktq.superfilemanager.cba"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyBTWVSmiNwHeN11xx8J9Y9-YKv-g1g0pHc"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
```
\ No newline at end of file
......@@ -15,6 +15,7 @@ android {
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
setProperty("archivesBaseName", "superfilemanager-v${versionName}")
}
buildTypes {
......@@ -30,6 +31,10 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
aidl true
}
}
dependencies {
......@@ -42,4 +47,10 @@ dependencies {
testImplementation libs.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
implementation("com.blankj:utilcodex:1.31.1")
implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation("com.github.JavaNoober.BackgroundLibrary:libraryx:1.7.6")
}
\ No newline at end of file
......@@ -2,25 +2,48 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Smartfilemanager"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
android:name=".activity.splash.SplashActivity"
android:exported="true"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.MainActivity"
android:exported="true"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity">
</activity>
<activity
android:name=".activity.FileCategoryActivity"
android:exported="false"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
</application>
</manifest>
\ No newline at end of file
package com.base.smartfilemanager
import android.app.Application
abstract class BaseApplication : Application() {
companion object {
lateinit var context: BaseApplication
}
final override fun onCreate() {
super.onCreate()
context = this
init()
}
abstract fun init()
}
\ No newline at end of file
package com.base.smartfilemanager
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
}
\ No newline at end of file
package com.base.smartfilemanager
class MyApplication : BaseApplication() {
override fun init() {
}
}
\ No newline at end of file
package com.base.smartfilemanager.activity
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import com.base.smartfilemanager.R
import com.base.smartfilemanager.adapter.FileBrowseAdapter
import com.base.smartfilemanager.bean.FileBean
import com.base.smartfilemanager.bean.FileCategoryBean
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.APK
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.Audio
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.DOC
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.Image
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.PDF
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.PPT
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.TXT
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.Video
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.XLS
import com.base.smartfilemanager.bean.FileCategoryBean.Companion.ZIP
import com.base.smartfilemanager.databinding.ActivityFileCategoryBinding
import com.base.smartfilemanager.helps.BaseActivity
import com.base.smartfilemanager.helps.file.FileEx.deleteDirectory
import com.base.smartfilemanager.helps.file.FileHelps
import com.base.smartfilemanager.view.FileDeleteDialog
import com.base.smartfilemanager.view.FileDeleteDialog.showFileDeleteDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.io.File
import java.lang.Exception
class FileCategoryActivity : BaseActivity<ActivityFileCategoryBinding>() {
private var type: String = ""
private lateinit var adapter: FileBrowseAdapter
override val binding: ActivityFileCategoryBinding by lazy {
ActivityFileCategoryBinding.inflate(layoutInflater)
}
override fun initView() {
type = intent.extras?.getString("type") ?: ""
binding.tvTittle.text = type
adapter = FileBrowseAdapter { size ->
binding.tvDelete.isSelected = size > 0
binding.tvDelete.isEnabled = size > 0
}
binding.rv.adapter = adapter
binding.rv.addItemDecoration(DividerItemDecoration(this, LinearLayout.VERTICAL))
binding.flBack.setOnClickListener {
finishToMain()
}
binding.tvDelete.setOnClickListener {
val deleteList = adapter.getSelect()
showFileDeleteDialog {
binding.clOther.visibility = View.VISIBLE
binding.progressbar.visibility = View.VISIBLE
lifecycleScope.launch(Dispatchers.Main) {
async {
try {
deleteList.forEach {
val deleteFile = File(it.path)
if (deleteFile.isFile) deleteFile.delete() else deleteDirectory(deleteFile)
}
} catch (e: Exception) {
}
binding.clOther.visibility = View.GONE
adapter.removeData(deleteList)
}.await()
}
}
}
}
override fun onStart() {
super.onStart()
checkPermission()
}
override fun onPermissionsResult(isGranted: Boolean) {
if (isGranted) {
initData()
}
}
private fun initData() {
val showList = HashSet<FileBean>()
binding.clOther.visibility = View.VISIBLE
binding.progressbar.visibility = View.VISIBLE
FileHelps.getFileList(
onUpdate = {
if (FileHelps.fileList.isNotEmpty()){
when (type) {
FileCategoryBean.ALL -> showList.addAll(FileHelps.fileList.filter { it.isDoc() })
XLS -> showList.addAll(FileHelps.fileList.filter { it.isXls() })
DOC -> showList.addAll(FileHelps.fileList.filter { it.isDocx() })
PDF -> showList.addAll(FileHelps.fileList.filter { it.isPdf() })
ZIP -> showList.addAll(FileHelps.fileList.filter { it.isZip() })
TXT -> showList.addAll(FileHelps.fileList.filter { it.isTxt() })
PPT -> showList.addAll(FileHelps.fileList.filter { it.isPpt() })
Video -> showList.addAll(FileHelps.fileList.filter { it.isVideo() })
Audio -> showList.addAll(FileHelps.fileList.filter { it.isAudio() })
APK -> showList.addAll(FileHelps.fileList.filter { it.isApk() })
Image -> showList.addAll(FileHelps.fileList.filter { it.isImage() })
}
if (showList.isNotEmpty()) {
binding.clOther.visibility = View.GONE
binding.progressbar.visibility = View.GONE
}
adapter.setData(showList.toList())
}
}, onEnd = {
if (showList.isEmpty()) {
binding.progressbar.visibility = View.GONE
binding.ivEmpty.visibility = View.VISIBLE
binding.tvDelete.visibility = View.GONE
}
})
}
}
\ No newline at end of file
package com.base.smartfilemanager.activity
import android.graphics.Typeface
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.base.smartfilemanager.fragment.FileCategoryFragment
import com.base.smartfilemanager.databinding.ActivityMainBinding
import com.base.smartfilemanager.helps.BaseActivity
class MainActivity : BaseActivity<ActivityMainBinding>() {
override val isFinishMain: Boolean = false
override val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val fileCategoryFragment by lazy {
FileCategoryFragment()
}
override fun initView() {
binding.idVp.run {
adapter = object : FragmentStateAdapter(this@MainActivity) {
override fun getItemCount(): Int {
return 1
}
override fun createFragment(position: Int): Fragment {
return fileCategoryFragment
}
}
}
binding.idVp.registerOnPageChangeCallback(object :
ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
setPage(position)
}
})
binding.idVp.isUserInputEnabled = false
}
private fun setPage(position: Int) {
binding.idBottom1.isSelected = true
binding.idTvHome.typeface = Typeface.DEFAULT_BOLD
}
override fun onPermissionsResult(isGranted: Boolean) {
if (isGranted) {
fileCategoryFragment.refreshRecent()
}
}
}
\ No newline at end of file
package com.base.smartfilemanager.activity.splash
import android.os.Handler
import com.base.smartfilemanager.databinding.ActivitySplashBinding
import com.blankj.utilcode.util.SPUtils
class ProgressManager {
private val binding: ActivitySplashBinding
private var mHandler: Handler
private var mIsPaused = false
private var mProgress = 0
private val listener: ProgressListener
constructor(binding: ActivitySplashBinding, listener: ProgressListener) {
this.binding = binding
this.listener = listener
mHandler = Handler()
initView()
}
private fun initView() {
val loadTime = SPUtils.getInstance().getInt("loading_page_time", 9)
binding.pb.max = loadTime
binding.pb.progress = 0
}
fun startProgress() {
val mRunnable: Runnable = object : Runnable {
override fun run() {
if (!mIsPaused) {
mProgress++ // 计算进度
binding.pb.progress = mProgress
if (mProgress < 9) {
mHandler.postDelayed(this, 1000) // 每秒钟更新一次进度
} else {
listener.onProgressMax()
pauseProgress()
}
}
}
}
mHandler.postDelayed(mRunnable, 1000)
}
fun pauseProgress() {
if (!mIsPaused) {
mIsPaused = true
mHandler.removeCallbacksAndMessages(null)
}
}
fun maxProgress() {
binding.pb.progress = binding.pb.max
listener.onProgressMax()
}
fun resumeProgress() {
if (mIsPaused) {
mIsPaused = false
startProgress()
}
}
interface ProgressListener {
fun onProgressMax()
}
}
\ No newline at end of file
package com.base.smartfilemanager.activity.splash
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Handler
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.base.smartfilemanager.R
import com.base.smartfilemanager.activity.MainActivity
import com.base.smartfilemanager.databinding.ActivitySplashBinding
import com.base.smartfilemanager.helps.BaseActivity
import com.blankj.utilcode.util.BarUtils
@SuppressLint("CustomSplashScreen")
class SplashActivity : BaseActivity<ActivitySplashBinding>(), ProgressManager.ProgressListener {
override val binding: ActivitySplashBinding by lazy {
ActivitySplashBinding.inflate(layoutInflater)
}
private var mProgressManager: ProgressManager? = null
private fun initStatusBar() {
BarUtils.setStatusBarLightMode(this, true)
BarUtils.setStatusBarColor(this, Color.TRANSPARENT)
if (Build.VERSION.SDK_INT >= 33) {
registerForActivityResult(ActivityResultContracts.RequestPermission()) {}.launch(
Manifest.permission.POST_NOTIFICATIONS
)
}
}
override fun initView() {
initStatusBar()
if (isDestroyed) {
return
}
mProgressManager = ProgressManager(binding, this)
mProgressManager?.startProgress()
}
override fun onProgressMax() {
Handler().postDelayed({
startActivity(Intent(this, MainActivity::class.java))
finish()
}, 500)
}
}
\ No newline at end of file
package com.base.smartfilemanager.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.smartfilemanager.R
import com.base.smartfilemanager.bean.FileBean
import com.base.smartfilemanager.databinding.ItemFileListBinding
import com.base.smartfilemanager.view.XmlEx.inflate
import com.bumptech.glide.Glide
class FileBrowseAdapter(private val select: (size: Int) -> Unit) : RecyclerView.Adapter<FileBrowseAdapter.FB>() {
private val fileList = arrayListOf<FileBean>()
inner class FB(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FB {
return FB(R.layout.item_file_list.inflate(parent))
}
override fun onBindViewHolder(holder: FB, position: Int) {
}
override fun getItemCount(): Int {
return fileList.size
}
override fun onBindViewHolder(holder: FB, position: Int, payloads: MutableList<Any>) {
val binding = ItemFileListBinding.bind(holder.itemView)
val context = holder.itemView.context
val data = fileList[position]
if (payloads.isEmpty()) {
data.loadImageIcon(context, binding.ivIcon)
binding.tvName.text = data.name
binding.tvPath.text = data.path
binding.ivSelector.isSelected = data.isSelect
binding.root.setOnClickListener {
data.isSelect = !data.isSelect
binding.ivSelector.isSelected = data.isSelect
notifyItemChanged(position, "局部刷洗")
select.invoke(fileList.filter { it.isSelect }.size)
}
} else {
binding.apply {
ivSelector.isSelected = data.isSelect
}
super.onBindViewHolder(holder, position, payloads)
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(data: List<FileBean>, clear: Boolean = false) {
if (clear) {
fileList.clear()
}
fileList.addAll(data)
notifyDataSetChanged()
}
fun FileBean.loadImageIcon(context: Context, ivIcon: ImageView) {
when {
isImage() or isVideo() -> Glide.with(context).load(path).into(ivIcon)
isAudio() -> ivIcon.setImageResource(R.drawable.audio)
isZip() -> ivIcon.setImageResource(R.drawable.zip)
isApk() -> ivIcon.setImageResource(R.drawable.apk)
isDocx() -> ivIcon.setImageResource(R.drawable.doc)
isPdf() -> ivIcon.setImageResource(R.drawable.pdf)
isXls() -> ivIcon.setImageResource(R.drawable.xls)
isTxt() -> ivIcon.setImageResource(R.drawable.txt)
isPpt() -> ivIcon.setImageResource(R.drawable.ppt)
}
}
fun getSelect(): List<FileBean> {
return fileList.filter { it.isSelect }
}
@SuppressLint("NotifyDataSetChanged")
fun removeData(deleteList: List<FileBean>) {
fileList.removeAll(deleteList.toSet())
notifyDataSetChanged()
}
}
package com.base.smartfilemanager.adapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.base.smartfilemanager.R
import com.base.smartfilemanager.bean.FileCategoryBean
import com.base.smartfilemanager.databinding.ItemFileCategoryDocBinding
import com.base.smartfilemanager.databinding.ItemFileCategoryMediaBinding
import com.base.smartfilemanager.databinding.ItemRecentMediaBinding
import com.base.smartfilemanager.view.XmlEx.inflate
import com.bumptech.glide.Glide
class FileCategoryAdapter(val mode: String, val click: (key: String) -> Unit) : RecyclerView.Adapter<FileCategoryAdapter.DC>() {
private val list = arrayListOf<FileCategoryBean>()
class DC(view: View) : ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DC {
return when (mode) {
MODE_DOCUMENT -> DC(R.layout.item_file_category_doc.inflate(parent))
MODE_MEDIA -> DC(R.layout.item_file_category_media.inflate(parent))
MODE_RECENT -> DC(R.layout.item_recent_media.inflate(parent))
else -> DC(R.layout.item_file_category_doc.inflate(parent))
}
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: DC, position: Int) {
val context = holder.itemView.context
val data = list[position]
when (mode) {
MODE_DOCUMENT -> {
val binding = ItemFileCategoryDocBinding.bind(holder.itemView)
binding.iv.setImageResource(data.drawable)
binding.tv.text = data.tittle
binding.root.setOnClickListener {
click.invoke(data.key)
}
}
MODE_MEDIA -> {
val binding = ItemFileCategoryMediaBinding.bind(holder.itemView)
binding.iv.setImageResource(data.drawable)
binding.tv.text = data.tittle
binding.root.setOnClickListener {
click.invoke(data.key)
}
}
MODE_RECENT -> {
val binding = ItemRecentMediaBinding.bind(holder.itemView)
Glide.with(context).load(data.url).centerCrop().into(binding.iv)
binding.root.setOnClickListener {
click.invoke(data.key)
}
}
}
}
@SuppressLint("NotifyDataSetChanged")
fun setData(dataList: List<FileCategoryBean>) {
list.clear()
list.addAll(dataList)
notifyDataSetChanged()
}
companion object {
val MODE_DOCUMENT = "document"
val MODE_MEDIA = "media"
val MODE_RECENT = "recent"
}
}
package com.base.smartfilemanager.bean
data class FileBean(
var name: String = "",
var path: String,
val size: Long,
val isDir: Boolean = false,
val type: String = "",
val time: Long = 0L,
var isSelect: Boolean = false
) {
fun isEmptyDir() = isDir
fun isImage() = !isDir && type in listOf("png", "jpg", "gif")
fun isVideo() = !isDir && type in listOf("mp4")
fun isAudio() = !isDir && type in listOf("mp3")
fun isLargeFile() = !isDir && size >= 10 * 1024 * 1024
fun isZip() = !isDir && type in listOf("zip", "tar", "7z")
fun isApk() = !isDir && type in listOf("apk")
fun isDoc() = !isDir && type in listOf("pdf", "doc", "docx", "xls", "ppt", "txt")
fun isDocx() = !isDir && type in listOf("docx")
fun isPdf() = !isDir && type in listOf("pdf")
fun isXls() = !isDir && type in listOf("xls")
fun isTxt() = !isDir && type in listOf("txt")
fun isPpt() = !isDir && type in listOf("ppt")
fun isOther() = !isImage() && !isVideo() && !isAudio() && !isZip() && !isApk()
fun isJunk() = !isDir && type in listOf("tmp", "cache", "temp")
fun isOtherTrash() =
!isDir && type in listOf("log", "bak", "old", "chk", "gid", "dmp", "thumb", "crdownload", "part", "trash", "trashes")
}
\ No newline at end of file
package com.base.smartfilemanager.bean
data class FileCategoryBean(
val key: String="",
val drawable: Int=0,
val tittle: String="",
val url:String="",
val path:String=""
) {
companion object {
const val ALL = "ALL"
const val XLS = "XLS"
const val DOC = "DOC"
const val PDF = "PDF"
const val ZIP = "ZIP"
const val TXT = "TXT"
const val PPT = "PPT"
const val Video = "Video"
const val Audio = "Audio"
const val APK = "APK"
const val Image = "Image"
}
}
\ No newline at end of file
package com.base.smartfilemanager.bean
data class ImageDataBean(
val path: String,
val hashCode: String,
val avgPixel: Int,
var isSelect: Boolean = false
)
\ No newline at end of file
package com.base.smartfilemanager.fragment
import android.content.Intent
import android.os.Build
import android.os.Environment
import android.view.View
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.base.smartfilemanager.R
import com.base.smartfilemanager.activity.FileCategoryActivity
import com.base.smartfilemanager.activity.MainActivity
import com.base.smartfilemanager.adapter.FileCategoryAdapter
import com.base.smartfilemanager.bean.FileCategoryBean
import com.base.smartfilemanager.databinding.FragmentFileCategoryBinding
import com.base.smartfilemanager.helps.BaseFragment
import com.base.smartfilemanager.helps.file.MediaStoreEx
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.PermissionUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
class FileCategoryFragment : BaseFragment<FragmentFileCategoryBinding>() {
override val binding: FragmentFileCategoryBinding by lazy {
FragmentFileCategoryBinding.inflate(layoutInflater)
}
private lateinit var docAdapter: FileCategoryAdapter
private lateinit var mediaAdapter: FileCategoryAdapter
private lateinit var recentAdapter: FileCategoryAdapter
private val docList = listOf(
FileCategoryBean(FileCategoryBean.ALL, R.drawable.all, FileCategoryBean.ALL),
FileCategoryBean(FileCategoryBean.XLS, R.drawable.xls, FileCategoryBean.XLS),
FileCategoryBean(FileCategoryBean.DOC, R.drawable.doc, FileCategoryBean.DOC),
FileCategoryBean(FileCategoryBean.ZIP, R.drawable.zip, FileCategoryBean.ZIP),
FileCategoryBean(FileCategoryBean.TXT, R.drawable.txt, FileCategoryBean.TXT),
FileCategoryBean(FileCategoryBean.PPT, R.drawable.ppt, FileCategoryBean.PPT),
FileCategoryBean(FileCategoryBean.PDF, R.drawable.pdf, FileCategoryBean.PDF),
)
private val mediaList = listOf(
FileCategoryBean(FileCategoryBean.Video, R.drawable.video, FileCategoryBean.Video),
FileCategoryBean(FileCategoryBean.Audio, R.drawable.audio, FileCategoryBean.Audio),
FileCategoryBean(FileCategoryBean.APK, R.drawable.apk, FileCategoryBean.APK),
FileCategoryBean(FileCategoryBean.Image, R.drawable.image, FileCategoryBean.Image),
)
override fun setView() {
docAdapter = FileCategoryAdapter(FileCategoryAdapter.MODE_DOCUMENT, ::itemClick)
binding.rvDocument.layoutManager = GridLayoutManager(requireContext(), 4).apply {
scrollToPositionWithOffset(0, 0)
}
binding.rvDocument.adapter = docAdapter
docAdapter.setData(docList)
mediaAdapter = FileCategoryAdapter(FileCategoryAdapter.MODE_MEDIA, ::itemClick)
binding.rvMedia.adapter = mediaAdapter
mediaAdapter.setData(mediaList)
binding.tvAuthorization.setOnClickListener {
(requireActivity() as MainActivity).checkPermission(true)
}
binding.llAll.setOnClickListener {
(requireActivity() as MainActivity).checkPermission(true)
}
recentAdapter = FileCategoryAdapter(FileCategoryAdapter.MODE_RECENT, ::itemClick)
binding.rvRecent.adapter = recentAdapter
}
override fun onStart() {
super.onStart()
if (checkStorage()) {
binding.llPermission.visibility = View.GONE
binding.tvAuthorization.visibility = View.GONE
binding.rvRecent.visibility = View.VISIBLE
loadRecentImageVideo()
}
}
private fun itemClick(key: String) {
requireContext().startActivity(Intent(requireContext(), FileCategoryActivity::class.java).apply {
putExtra("type", key)
})
}
private fun checkStorage(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
PermissionUtils.isGranted(PermissionConstants.STORAGE)
}
}
fun refreshRecent() {
if (isVisible) {
binding.llPermission.visibility = View.GONE
binding.rvRecent.visibility = View.VISIBLE
loadRecentImageVideo()
}
}
private fun loadRecentImageVideo(
) = lifecycleScope.launch(Dispatchers.Main) {
val imageList = arrayListOf<FileCategoryBean>()
async {
val list = MediaStoreEx.recentImage(requireContext()).map { FileCategoryBean(url = it.toString()) }
imageList.addAll(list)
}.await()
if (imageList.isEmpty()) {
binding.llEmpty.visibility = View.VISIBLE
} else {
recentAdapter.setData(imageList)
}
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps
import android.util.Base64
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
object AESHelper {
private const val aesKey = "nbutdub5lsfdkitt"
private val cipher by lazy {
Cipher.getInstance("AES/GCM/NoPadding")
}
fun encrypt(content: String): String {
try {
val iv = ByteArray(12).apply {
SecureRandom().nextBytes(this)
}
val contentBytes = content.toByteArray(Charsets.UTF_8)
val params = GCMParameterSpec(128, iv)
cipher.init(
Cipher.ENCRYPT_MODE,
secretKey, params
)
val encryptData = cipher.doFinal(contentBytes)
assert(encryptData.size == contentBytes.size + 16)
val message = ByteArray(12 + contentBytes.size + 16)
System.arraycopy(iv, 0, message, 0, 12)
System.arraycopy(encryptData, 0, message, 12, encryptData.size)
return String(Base64.encode(message, Base64.NO_WRAP), Charsets.UTF_8)
} catch (_: Exception) {
}
return content
}
@Synchronized
fun decrypt(content: String): String {
try {
val con = content.replace(" ".toRegex(), "+")
val contentByte = Base64.decode(con, Base64.NO_WRAP)
require(contentByte.size >= 12 + 16)
val params = GCMParameterSpec(128, contentByte, 0, 12)
cipher.init(
Cipher.DECRYPT_MODE,
secretKey, params
)
val decryptData = cipher.doFinal(contentByte, 12, contentByte.size - 12)
return String(decryptData, Charsets.UTF_8)
} catch (_: Exception) {
}
return content
}
private val secretKey by lazy {
SecretKeySpec(aesKey.toByteArray(), "AES")
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps
import android.content.Context
import android.util.AttributeSet
import com.base.smartfilemanager.helps.KotlinExt.decode
import com.noober.background.view.BLTextView
class AESTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : BLTextView(context, attrs) {
override fun setText(text: CharSequence?, type: BufferType?) {
super.setText(text?.toString()?.run { decode().takeIf { it != this } } ?: text, type)
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps
import android.app.AppOpsManager
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.base.smartfilemanager.activity.MainActivity
import com.base.smartfilemanager.view.DialogViews
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.BarUtils
import com.blankj.utilcode.util.PermissionUtils
import org.json.JSONObject
abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
protected abstract val binding: T
protected open val isLightMode: Boolean? = null
protected open val isFinishMain: Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
isLightMode?.let {
BarUtils.setStatusBarLightMode(this, it)
}
// EventHelper.event("page_${javaClass.simpleName}")
initView()
initListener()
}
protected abstract fun initView()
protected open fun initListener() {}
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
onPermissionsResult(Environment.isExternalStorageManager())
}
}
open fun onPermissionsResult(isGranted: Boolean) {
val obj = JSONObject()
obj.put("activity", javaClass.simpleName)
if (isGranted) {
// EventHelper.event("permission_allow", ext = obj)
} else {
// EventHelper.event("permission_deny", ext = obj)
}
}
private var isRequested = false
fun checkPermission(needCheck: Boolean = false) {
if (needCheck) {
onPermissionsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
PermissionUtils.isGranted(PermissionConstants.STORAGE)
}
)
} else {
if (isRequested) {
onPermissionsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
PermissionUtils.isGranted(PermissionConstants.STORAGE)
}
)
return
}
isRequested = true
// requestPermissionCount--
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
onPermissionsResult(true)
} else {
if (isFinishing || isDestroyed) {
return
}
dialog = DialogViews.showGerPermission(this, {
val intent =
Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${packageName}")
permissionLauncher.launch(intent)
}, {
if (isFinishMain) {
finishToMain()
}
})
}
} else {
PermissionUtils.permissionGroup(PermissionConstants.STORAGE)
.callback { isAllGranted, _, _, _ ->
if (isAllGranted) {
onPermissionsResult(true)
} else {
onPermissionsResult(false)
}
}.request()
}
}
protected fun finishToMain() {
if (this !is MainActivity && !ActivityUtils.isActivityExistsInStack(MainActivity::class.java)) {
startActivity(Intent(this, MainActivity::class.java))
}
finish()
}
private var isRequestedAccess = false
private val accessLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
}
fun checkAccesSettings(needcheck: Boolean = false) {
val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
if (needcheck) {
onAccesSettingsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
} else {
appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
}
)
} else {
if (isRequestedAccess) {
onAccesSettingsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
} else {
appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
}
)
return
}
isRequestedAccess = true
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (appOpsManager.unsafeCheckOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
) {
onAccesSettingsResult(true)
} else {
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${this.packageName}")
accessLauncher.launch(intent)
}
} else {
if (appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
this.packageName
) == AppOpsManager.MODE_ALLOWED
) {
onAccesSettingsResult(true)
} else {
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse("package:${this.packageName}")
accessLauncher.launch(intent)
}
}
}
open fun onAccesSettingsResult(isGranted: Boolean) {
val obj = JSONObject()
obj.put("activity", javaClass.simpleName)
if (isGranted) {
// EventHelper.event("permission_allow", ext = obj)
} else {
// EventHelper.event("permission_deny", ext = obj)
}
}
var dialog: Dialog? = null
override fun onDestroy() {
super.onDestroy()
if (dialog != null) {
dialog?.dismiss()
}
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
abstract class BaseFragment<T : ViewBinding> : Fragment() {
protected abstract val binding: T
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setView()
setListener()
}
protected abstract fun setView()
protected open fun setListener() {}
}
\ No newline at end of file
package com.base.smartfilemanager.helps
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.SPUtils
object ConfigHelper {
var gid = ""
var isOpenNotification = false
var appList: List<AppUtils.AppInfo>? = null
// 域名
const val eventUrl = "https://rp.cansole764cansole.xyz"
const val apiUrl = "https://api.cansole764cansole.xyz"
// admob广告id
const val openAdmobId = "/6499/example/app-open"
const val interAdmobId = "ca-app-pub-3940256099942544/1033173712"
const val nativeAdmobId = "ca-app-pub-3940256099942544/2247696110"
// 正式包名
const val packageName = "com.kk.cleanmaster.file.cleanmaster.master"
val noLoadingActivities = listOf(
"full", // 过滤全屏广告
"adActivity",
// 返回前台时不跳转启动页的 activity
)
var junkSizeClean = 0L
get() {
return SPUtils.getInstance().getLong("junkSizeClean", field)
}
set(value) {
field = value
SPUtils.getInstance().put("junkSizeClean", value)
}
var ifAgreePrivacy = false
get() {
return SPUtils.getInstance().getBoolean("ifAgreePrivacy", field)
}
set(value) {
field = value
SPUtils.getInstance().put("ifAgreePrivacy", value, true)
}
var ifGuest = false
get() {
return SPUtils.getInstance().getBoolean("ifGuest", field)
}
set(value) {
field = value
SPUtils.getInstance().put("ifGuest", value, true)
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps
import android.os.Build
import android.util.Log
import com.base.smartfilemanager.helps.ConfigHelper.ifAgreePrivacy
import com.blankj.utilcode.BuildConfig
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.DeviceUtils
import com.blankj.utilcode.util.SPUtils
import com.blankj.utilcode.util.ScreenUtils
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONObject
import java.io.IOException
object EventHelper {
private val TAG = "EventHelper"
private val url by lazy {
val pkg = AppUtils.getAppPackageName()
val url = StringBuilder(
"${ConfigHelper.eventUrl}/${
pkg.filter { it.isLowerCase() }.substring(4, 9)
}sp"
)
url.append("?pkg=$pkg")
url.toString()
}
fun event(
key: String,
value: String? = null,
ext: JSONObject? = null,
isSingleEvent: Boolean = false
) {
if (!ifAgreePrivacy) {
Log.e(TAG, "ifAgreePrivacy=$ifAgreePrivacy")
return
}
if (isSingleEvent) {
val stringSet = SPUtils.getInstance().getStringSet("singleEvent")
if (stringSet.contains(key)) {
return
}
}
val pkg = AppUtils.getAppPackageName()
val s = JSONObject()
.put("action", key)
.put("value", value)
.put("ext", ext)
val s2 = JSONObject()
.put("${pkg}_1", "${ScreenUtils.getScreenHeight()}")
.put("${pkg}_2", "${ScreenUtils.getScreenWidth()}")
.put("${pkg}_3", DeviceUtils.getModel())
.put("${pkg}_4", Build.MANUFACTURER)
.put("${pkg}_5", Build.VERSION.SDK_INT)
.put("${pkg}_8", AppUtils.getAppVersionName())
.put("${pkg}_9", DeviceUtils.getAndroidID())
.put("${pkg}_10", ConfigHelper.gid)
.put("${pkg}_11", System.getProperty("http.agent"))
.put("${pkg}_13", "android")
.put("${pkg}_14", "${AppUtils.getAppVersionCode()}")
.put("${pkg}_15", "google")
.put("${pkg}_24", BuildConfig.BUILD_TYPE)
val data = JSONObject()
.put("data", s)
.put("bp", s2)
.toString()
val body = AESHelper.encrypt(data)
.toRequestBody("application/json;charset=utf-8".toMediaTypeOrNull())
val client = OkHttpClient.Builder().apply {
if (BuildConfig.DEBUG) {
addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
}
}.build()
val request = Request.Builder()
.url(url)
.post(body)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
Log.d("TAG", "event: $key")
if (isSingleEvent) {
val stringSet =
SPUtils.getInstance().getStringSet("singleEvent", mutableSetOf())
stringSet.add(key)
SPUtils.getInstance().put("singleEvent", stringSet)
}
}
})
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps
import android.view.View
import com.base.smartfilemanager.BaseApplication
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.Locale
object KotlinExt {
private val aesMap = mutableMapOf<Int, String>()
fun Int.string(vararg arg: Any) = try {
(aesMap[this] ?: BaseApplication.context.getString(this).decode()).run {
aesMap[this@string] = this
String.format(this, *arg)
}
} catch (_: Exception) {
""
}
fun String.decode() = AESHelper.decrypt(this)
.replace("\\r", "\r")
.replace("\\n", "\n")
.replace("\\'", "'")
.replace("\\\"", "\"")
.replace("\\?", "?")
.replace("&amp;", "&")
fun Collection<View>.setOnClickListener(listener: (View) -> Unit) {
this.forEach {
it.setOnClickListener(listener)
}
}
fun View.setTrackedOnClickListener(action: (view: View) -> Unit) {
setOnClickListener {
action(this)
var view: View? = this
while (view != null) {
try {
val obj = JSONObject()
obj.put("view_id", resources.getResourceEntryName(view.id))
EventHelper.event("click_id", ext = obj)
break
} catch (_: Exception) {
view = view.parent as? View
}
}
}
}
fun Number.toFormatSize(count: Int = 1): String {
var suffix = "B"
var fSize = this.toDouble()
if (fSize > 1024) {
suffix = "KB"
fSize /= 1024.0
}
if (fSize > 1024) {
suffix = "MB"
fSize /= 1024.0
}
if (fSize > 1024) {
suffix = "GB"
fSize /= 1024.0
}
return String.format("%.${count}f %s", fSize, suffix)
}
fun Long.toFormatTime(): String {
return SimpleDateFormat("MMM/dd/yyyy", Locale.getDefault()).format(this)
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps.file
import java.io.File
import java.util.Stack
object FileEx {
/**
*删除文件夹
*/
fun deleteDirectory(dir: File): Boolean {
val stackFiles = Stack<File>()
stackFiles.push(dir)
while (stackFiles.size > 0) {
val currentFile = stackFiles.peek()
val subFiles = currentFile.listFiles()
if (subFiles != null) {
for (i in subFiles.indices) {
if (subFiles[i].isFile) {
if (!subFiles[i].delete()) {
return false
}
} else {
stackFiles.push(subFiles[i])
}
}
}
if (currentFile === stackFiles.peek()) {
if (!currentFile.delete()) {
return false
}
stackFiles.pop()
}
}
return true
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps.file
import android.os.Environment
import android.provider.MediaStore
import android.text.TextUtils
import com.base.smartfilemanager.BaseApplication
import com.base.smartfilemanager.bean.FileBean
import com.base.smartfilemanager.bean.ImageDataBean
import com.blankj.utilcode.util.FileUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.random.Random
object FileHelps {
var usedSize = 0L //已经用的
var freeSize = 0L //剩余大小
var systemSize = 0L //系统占用
var allSize = 0L //所有大小
init {
val root = Environment.getExternalStorageDirectory()
freeSize = FileUtils.getFsAvailableSize(root.absolutePath)
allSize = FileUtils.getFsTotalSize(root.absolutePath)
usedSize = allSize - freeSize
systemSize = FileUtils.getFsTotalSize(Environment.getRootDirectory().absolutePath)
}
private val dateFormat by lazy {
SimpleDateFormat("MMMM dd,yyyy", Locale.getDefault())
}
val fileList = mutableListOf<FileBean>()
private fun initFileList() {
if (fileList.isEmpty()) {
initFileList(Environment.getExternalStorageDirectory())
}
}
fun deleteFile(fileOrDirectory: File): Boolean {
if (fileOrDirectory.isDirectory)
fileOrDirectory.listFiles()?.forEach { child -> deleteFile(child) }
return fileOrDirectory.delete()
}
fun getFileList(onUpdate: () -> Unit, onEnd: (() -> Unit)? = null) {
MainScope().launch(Dispatchers.Main) {
launch(Dispatchers.IO) {
initFileList()
}
var emptyCount = 0
var preSize = 0
while (emptyCount < 12) {
delay(Random.nextLong(400, 800))
if (fileList.isNotEmpty()) {
if (fileList.size - preSize == 0) {
emptyCount++
} else {
emptyCount = 0
onUpdate()
}
preSize = fileList.size
}
}
onEnd?.invoke()
}
}
private fun initFileList(file: File) {
if (file.isFile) {
fileList.add(file.toBean())
} else {
file.listFiles()?.let {
if (it.isEmpty()) {
fileList.add(file.toBean())
} else {
for (i in it) {
initFileList(i)
}
}
}
}
}
fun File.toBean() = FileBean(
name = name,
path = absolutePath,
size = length(),
isDir = isDirectory,
type = if (TextUtils.isEmpty(
FileUtils.getFileExtension(name).lowercase()
)
) "unKnow" else FileUtils.getFileExtension(name).lowercase(),
time = lastModified()
)
val similarImageList = mutableListOf<ImageDataBean>()
fun getImageFiles(callback: () -> Unit) {
MainScope().launch(Dispatchers.Main) {
withContext(Dispatchers.IO) {
val imageList = mutableListOf<String>()
val query = BaseApplication.context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
arrayOf(MediaStore.Files.FileColumns.DATA),
null,
null,
"${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
) ?: return@withContext
while (query.moveToNext()) {
imageList.add(query.getString(0))
}
query.close()
val imageDataList = mutableListOf<ImageDataBean>()
imageDataList.addAll(imageList.mapNotNull { ImageHelper.createImage(it) })
imageDataList.sortBy { it.hashCode }
similarImageList.clear()
var index1 = 0
var index2 = 1
val similarItem = mutableListOf<ImageDataBean>()
while (index2 < imageDataList.size) {
val first = imageDataList[index1]
val second = imageDataList[index2]
if (ImageHelper.similarCondition(first, second)) {
if (similarItem.indexOf(first) < 0) {
similarItem.add(first)
}
if (similarItem.indexOf(second) < 0) {
similarItem.add(second)
}
} else {
if (similarItem.size > 1) {
similarImageList.addAll(similarItem)
}
similarItem.clear()
}
index1++
index2++
}
if (similarItem.size > 1) {
similarImageList.addAll(similarItem)
}
}
callback()
}
}
val typeDateList = mutableListOf<Pair<String, MutableList<FileBean>>>()
fun getTypeFiles(callback: () -> Unit) {
MainScope().launch(Dispatchers.Main) {
withContext(Dispatchers.IO) {
val typeFiles = mutableListOf<String>()
val query = BaseApplication.context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
arrayOf(MediaStore.Files.FileColumns.DATA),
null,
null,
"${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
) ?: return@withContext
while (query.moveToNext()) {
typeFiles.add(query.getString(0))
}
query.close()
typeDateList.clear()
typeFiles.forEach {
val fileBean = File(it).toBean()
val dateString = dateFormat.format(fileBean.time)
if (typeDateList.lastOrNull()?.first != dateString) {
typeDateList.add(Pair(dateString, mutableListOf()))
}
typeDateList.lastOrNull()?.second?.add(fileBean)
}
callback()
}
}
}
}
\ No newline at end of file
package com.base.smartfilemanager.helps.file
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.ThumbnailUtils
import com.base.smartfilemanager.bean.ImageDataBean
import com.blankj.utilcode.util.ScreenUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.math.pow
object ImageHelper {
private val width by lazy {
ScreenUtils.getScreenWidth().coerceAtMost(1024)
}
private val height by lazy {
ScreenUtils.getScreenHeight().coerceAtMost(1280)
}
private fun loadBitmapFromFile(path: String): Bitmap? {
if (width <= 0 || 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() / height.toFloat()).coerceAtLeast(0.5f + options.outWidth.toFloat() / width.toFloat()).toInt()
inSampleSize += 1
options.inSampleSize = inSampleSize.coerceAtLeast(1)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, options)
}
suspend fun createImage(path: String): ImageDataBean? {
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 ImageDataBean(path, hashCode.toString(), avgPixel)
} 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: ImageDataBean, second: ImageDataBean): Boolean {
return hammingDistance(first.hashCode, second.hashCode) <= 6 && (first.avgPixel.toFloat() / second.avgPixel) in 0.8..1.2
}
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
package com.base.smartfilemanager.helps.file
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.MediaStore
object MediaStoreEx {
fun recentImage(context: Context): ArrayList<Uri> {
val list = arrayListOf<Uri>()
var cursor: Cursor? = null
// 查询照片的Uri和字段
val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME)
//DESC 降列
//ASC 升序
val sortOrder = MediaStore.Images.ImageColumns._ID + " DESC"
try {
// 执行查询
cursor = context.contentResolver.query(uri, projection, null, null, sortOrder)
// 遍历结果
if (cursor != null && cursor.moveToFirst()) {
do {
val id =
cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID))
list.add(ContentUris.withAppendedId(uri, id))
if (list.size == 10) {
break
}
} while (cursor.moveToNext())
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
cursor?.close()
}
return list
}
fun recentVideo(context: Context): ArrayList<Uri> {
val list = arrayListOf<Uri>()
var cursor: Cursor? = null
// 查询照片的Uri和字段
val uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME)
//DESC 降列
//ASC 升序
val sortOrder = MediaStore.Video.VideoColumns._ID + " DESC"
try {
// 执行查询
cursor = context.contentResolver.query(uri, projection, null, null, sortOrder)
// 遍历结果
if (cursor != null && cursor.moveToFirst()) {
do {
val id =
cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID))
list.add(ContentUris.withAppendedId(uri, id))
if (list.size == 10) {
break
}
} while (cursor.moveToNext())
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
cursor?.close()
}
return list
}
}
package com.base.smartfilemanager.view
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import com.base.smartfilemanager.R
import com.base.smartfilemanager.databinding.DialogPermissonOpenBinding
import com.blankj.utilcode.util.SpanUtils
object DialogViews {
fun showGerPermission(context: Context, onClose: (view: Dialog) -> Unit, onClose2: (view: Dialog) -> Unit?): Dialog {
val dialog = Dialog(context)
val binding = DialogPermissonOpenBinding.inflate(LayoutInflater.from(context))
dialog.requestWindowFeature(1)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.window?.setLayout(-1, -1)
dialog.setCanceledOnTouchOutside(false)
dialog.setCancelable(false)
dialog.setContentView(binding.root)
val appName = context.resources.getString(R.string.app_name)
SpanUtils.with(binding.idTvTt)
.append("Allow $appName to access ")
.setFontSize(13, true)
.setForegroundColor(0xFF999999.toInt())
.append("All Files Access ")
.setBold()
.setFontSize(13, true)
.setForegroundColor(0xFF4273FF.toInt())
.append("permission to manage files of your device?")
.setFontSize(13, true)
.setForegroundColor(0xFF999999.toInt())
.create()
binding.idDeny.setOnClickListener {
dialog.dismiss()
onClose2.invoke(dialog)
}
binding.idTvAllow.setOnClickListener {
dialog.dismiss()
onClose.invoke(dialog)
}
dialog.show()
return dialog
}
}
\ No newline at end of file
package com.base.smartfilemanager.view
import android.content.Context
import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import com.base.smartfilemanager.R
import com.base.smartfilemanager.databinding.DialogFileDeleteBinding
object FileDeleteDialog {
fun Context.showFileDeleteDialog(click: () -> Unit) {
val binding = DialogFileDeleteBinding.inflate(LayoutInflater.from(this))
val dialog = AlertDialog.Builder(this).setView(binding.root).create()
dialog.setContentView(binding.root)
dialog.setCanceledOnTouchOutside(true)
dialog.show()
//修改dialog的尺寸
val lp = dialog.window?.attributes
lp?.width = this.resources.getDimensionPixelOffset(R.dimen.dp_310)
lp?.height = ViewGroup.LayoutParams.WRAP_CONTENT
dialog.window?.attributes = lp
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
val text = "Selected files cannot be recovered after deleting, continue anyway?"
val spannableString = SpannableString(text)
val start = text.indexOf("cannot")
if (start != -1) {
val end = start + "cannot be recovered".length
val colorSpan = ForegroundColorSpan(ContextCompat.getColor(this, R.color.color_f04949)) // 使用资源文件中的颜色
spannableString.setSpan(colorSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
binding.tvDesc.text = spannableString
binding.tvDelete.setOnClickListener {
dialog.dismiss()
click.invoke()
}
binding.tvCancel.setOnClickListener {
dialog.dismiss()
}
}
}
\ No newline at end of file
package com.base.smartfilemanager.view
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
object XmlEx {
fun Int.inflate(parent: ViewGroup, attachToRoot: Boolean = false): View {
return LayoutInflater.from(parent.context).inflate(this, parent, attachToRoot)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/select_n" android:state_selected="false" />
<item android:drawable="@drawable/select_s" android:state_selected="true" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="8dp" />
<solid android:color="#0A0336" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="8dp" />
<gradient
android:endColor="#9477FF"
android:startColor="#00E0FF"
android:gradientRadius="10dp"/>
</shape>
</clip>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/white"/>
<item android:drawable="@drawable/splash_bg" />
<item
android:top="130dp"
android:gravity="top|center_horizontal">
<bitmap
android:src="@mipmap/icon_text" />
</item>
</layer-list>
\ No newline at end of file
<?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="#FDFDFD"
android:orientation="vertical"
tools:context=".activity.FileCategoryActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_action_bar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/white"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/fl_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingHorizontal="15dp"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:src="@drawable/fanhui"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="ContentDescription" />
</FrameLayout>
<TextView
android:id="@+id/tv_tittle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:text=""
android:textColor="#FF333333"
android:textSize="17sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/fl_back"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<com.noober.background.view.BLTextView
android:id="@+id/tv_delete"
android:layout_width="278dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:layout_marginBottom="30dp"
android:enabled="false"
android:gravity="center"
android:text="Delete"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold"
app:bl_corners_radius="24dp"
app:bl_selected_solid_color="#2D4059"
app:bl_unSelected_solid_color="#999999"
tools:ignore="HardcodedText" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_other"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<!--suppress AndroidElementNotAllowed -->
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:indeterminateTint="#0066FD"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/empty"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
\ No newline at end of file
......@@ -5,15 +5,58 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
android:orientation="vertical"
tools:context=".activity.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/id_vp"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/id_ll_nav"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/id_ll_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:elevation="10dp"
android:orientation="horizontal"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/id_bottom_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="15dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="visible">
<com.noober.background.view.BLImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:bl_selected_drawable="@drawable/tab_home_s"
app:bl_unSelected_drawable="@drawable/tab_home_s" />
<com.noober.background.view.BLTextView
android:id="@+id/id_tv_home"
android:layout_width="100dp"
android:layout_height="20dp"
android:layout_marginTop="2dp"
android:gravity="center"
android:text="Home"
android:textSize="13sp"
app:bl_selected_textColor="@color/color_2D4059"
app:bl_unSelected_textColor="@color/color_dadde5"
tools:ignore="HardcodedText" />
</androidx.appcompat.widget.LinearLayoutCompat>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?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:background="@drawable/splash_bp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.splash.SplashActivity">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="2" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/id_ll_jindu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="49dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<ProgressBar
android:id="@+id/pb"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="16dp"
android:layout_marginHorizontal="65dp"
android:progressDrawable="@drawable/shape_splash_s" />
<TextView
android:id="@+id/tv_load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:text="Loading..."
android:textColor="#000000"
android:textSize="15sp"
tools:ignore="HardcodedText" />
</androidx.appcompat.widget.LinearLayoutCompat>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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="300dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:cardCornerRadius="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/app_name"
android:textSize="15sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="Selected files cannot be recovered after deleting, continue anyway?"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="24dp"
android:layout_marginBottom="20dp">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:text="CANCEL"
android:textColor="#999999"
android:textSize="15sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DELETE"
android:textColor="#F24949"
android:textSize="15sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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_gravity="center"
android:orientation="vertical">
<com.noober.background.view.BLLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:orientation="vertical"
app:bl_corners_radius="10dp"
app:bl_solid_color="@color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="24dp"
android:text="Permission request"
android:textColor="#000000"
android:textSize="17sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/id_tv_tt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="22dp"
android:layout_marginBottom="38dp"
android:text="Request read permission to check recent picture"
android:textSize="13sp"
tools:ignore="HardcodedText" />
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="23dp"
android:layout_marginTop="40dp"
android:layout_marginBottom="14dp">
<com.noober.background.view.BLTextView
android:id="@+id/id_deny"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_marginEnd="13dp"
android:layout_weight="1"
android:gravity="center"
android:text="Deny"
android:textColor="#000000"
android:textSize="17sp"
app:bl_corners_radius="18dp"
app:bl_solid_color="#F5F5F5"
tools:ignore="HardcodedText" />
<com.noober.background.view.BLTextView
android:id="@+id/id_tv_allow"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:gravity="center"
android:text="Allow"
android:textColor="@color/white"
android:textSize="17sp"
app:bl_corners_radius="18dp"
app:bl_solid_color="#5b4fff"
tools:ignore="HardcodedText" />
</androidx.appcompat.widget.LinearLayoutCompat>
</com.noober.background.view.BLLinearLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
\ No newline at end of file
<?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:orientation="vertical"
tools:context=".activity.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="35dp"
android:text="Document tools"
android:textColor="@color/black"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="172dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
android:elevation="5dp"
app:cardCornerRadius="12dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_document"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:overScrollMode="never" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:text="Folder"
android:textColor="@color/black"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_media"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="6dp"
android:orientation="horizontal"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Recent images"
android:textColor="@color/black"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<LinearLayout
android:id="@+id/ll_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="UseCompoundDrawables">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:includeFontPadding="false"
android:text="ALL"
android:textColor="#999999"
android:textSize="15sp"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/jingru"
tools:ignore="ContentDescription" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="80dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_recent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginHorizontal="3dp"
android:orientation="horizontal"
android:overScrollMode="never"
android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<LinearLayout
android:id="@+id/ll_permission"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables,UselessParent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@drawable/filestu"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Permission"
android:textColor="#343434"
android:textSize="7sp"
tools:ignore="HardcodedText,SmallSp" />
</LinearLayout>
<com.noober.background.view.BLTextView
android:id="@+id/tv_authorization"
android:layout_width="209dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="50dp"
android:gravity="center"
android:text="Authorization"
android:textColor="@color/white"
android:textSize="10sp"
app:bl_corners_radius="12dp"
app:bl_solid_color="#2D4059"
tools:ignore="HardcodedText,SmallSp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal"
android:visibility="gone"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/recent_empty"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:text="There's no recent image yet"
tools:ignore="HardcodedText" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_marginVertical="10dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="25dp"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv"
android:layout_width="42dp"
android:layout_height="40dp"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:textColor="#333333"
android:textSize="12sp" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_marginVertical="10dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="23dp"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv"
android:layout_width="45dp"
android:layout_height="45dp"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:textColor="#333333"
android:textSize="12sp" />
</LinearLayout>
\ No newline at end of file
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_icon"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_gravity="center_vertical"
tools:ignore="ContentDescription" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="5dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true" />
<TextView
android:id="@+id/tv_path"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView
android:id="@+id/iv_selector"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="6dp"
android:src="@drawable/bg_file_selector"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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="70dp"
android:layout_height="70dp"
android:layout_margin="13dp"
android:elevation="0dp"
app:cardCornerRadius="4dp">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
</androidx.cardview.widget.CardView>
\ No newline at end of file
......@@ -2,4 +2,7 @@
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="color_dadde5">#DADDE5</color>
<color name="color_2D4059">#2D4059</color>
<color name="color_f04949">#f04949</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="dp_310">310dp</dimen>
</resources>
\ No newline at end of file
<resources>
<string name="app_name">smart-file-manager</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="app_name">Super File Manager</string>
</resources>
\ No newline at end of file
[versions]
agp = "8.3.0"
agp = "8.1.0"
kotlin = "1.9.0"
coreKtx = "1.10.1"
coreKtx = "1.9.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
......
#Thu May 30 09:42:03 CST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
......@@ -16,6 +16,11 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
maven { url "https://android-sdk.is.com" }
maven { url "https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea" }
maven { url "https://artifact.bytedance.com/repository/pangle" }
maven { url "https://s01.oss.sonatype.org/content/groups/public" }
}
}
......
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