Commit adf80db0 authored by wanglei's avatar wanglei Committed by wanglei

Initial commit

parent 8d64e36f
Pipeline #987 failed with stages
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
# Default ignored files
/shelf/
/workspace.xml
Solar Master Ace
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
/build
\ No newline at end of file
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
}
android {
namespace = "com.zxhy.solarmasterace"
compileSdk = 34
defaultConfig {
applicationId = "com.zxhy.solarmasterace"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.coil)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package com.zxhy.solarmasterace
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.zxhy.solarmasterace", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<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:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SolarMasterAce"
tools:targetApi="31">
<activity
android:name=".filebrowser.FileBrowserActivity"
android:exported="false" />
<activity
android:name=".dupfile.DupImageActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
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>
</application>
</manifest>
\ No newline at end of file
package com.zxhy.solarmasterace
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.widget.AdapterView
import android.widget.AdapterView.OnItemSelectedListener
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.navigation.findNavController
import com.google.android.material.navigation.NavigationBarView
import com.zxhy.solarmasterace.databinding.ActivityMainBinding
import com.zxhy.solarmasterace.permission.requestStoreFollow
import com.zxhy.solarmasterace.quicktools.ActivityLauncher
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var launcher: ActivityLauncher
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
launcher = ActivityLauncher(this)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
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
}
window.statusBarColor = ContextCompat.getColor(this, R.color.color_FF5C67E5)
//关键去除itemIconTint影响
binding.bNav.itemIconTintList = null
binding.bNav.setItemIconSizeRes(R.dimen.dp_24)
binding.bNav.setOnItemSelectedListener(object : OnItemSelectedListener,
NavigationBarView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.nav_manager -> {
window.statusBarColor =
ContextCompat.getColor(this@MainActivity, R.color.color_FF5C67E5)
runCatching {
findNavController(R.id.fragment_container_view).navigate(R.id.action_toolsFragment_to_managerFragment)
}
}
R.id.nav_tools -> {
window.statusBarColor =
ContextCompat.getColor(this@MainActivity, R.color.white)
runCatching {
findNavController(R.id.fragment_container_view).navigate(R.id.action_managerFragment_to_toolsFragment)
}
}
}
return true
}
})
requestStoreFollow(launcher, disAgreeAction = {}, agreeAction = {})
}
}
\ No newline at end of file
package com.zxhy.solarmasterace.adapter
import com.zxhy.solarmasterace.data.FileBean
interface AdapterCommonDataFunction {
abstract fun setData(data: List<FileBean>)
}
\ No newline at end of file
package com.zxhy.solarmasterace.adapter
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.zxhy.solarmasterace.databinding.ItemCardImage85Binding
class ItemImage85ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = ItemCardImage85Binding.bind(view)
}
\ No newline at end of file
package com.zxhy.solarmasterace.data
import android.net.Uri
import com.zxhy.solarmasterace.R
import java.io.File
data class FileBean(
val name: String = "",
val path: String = "",
val uri: Uri = Uri.EMPTY,
val size: Long = 0L,
val time: String = "",
var isSelect: Boolean = false
) {
fun file(): File {
return File(path)
}
fun isDir(): Boolean {
val file = File(path)
return file.exists() && file.isDirectory
}
fun isEmpty(): Boolean {
return size == 0L
}
fun isImage(): Boolean {
return name.contains(".jpg") or name.contains(".png") or name.contains(".gif")
}
fun imageRes(): Int {
return R.mipmap.as_230
}
fun isVideo(): Boolean {
return name.contains(".mp4") or name.contains(".avi")
}
fun isAudio(): Boolean {
return name.contains(".mp3") or name.contains(".avi")
}
fun isApk(): Boolean {
return name.contains(".apk")
}
fun isZip(): Boolean {
return name.contains(".zip") or name.contains(".rar")
}
fun isLogFile(): Boolean {
return name.contains("log") or name.contains("Log")
}
fun isWord(): Boolean {
return name.contains(".doc") or name.contains(".docx")
}
fun isExcel(): Boolean {
return name.contains(".xls") or name.contains(".xlsx")
}
fun isPPt(): Boolean {
return name.contains(".ppt") or name.contains(".pptx")
}
fun isPdf(): Boolean {
return name.contains(".pdf")
}
fun isTxt(): Boolean {
return name.contains(".txt")
}
fun isDocument(): Boolean {
return isWord() or isExcel() or isPPt() or isPdf() or isTxt()
}
}
This diff is collapsed.
package com.zxhy.solarmasterace.filebrowser
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.zxhy.solarmasterace.R
import com.zxhy.solarmasterace.databinding.ActivityFileBrowserBinding
class FileBrowserActivity : AppCompatActivity() {
private lateinit var binding: ActivityFileBrowserBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityFileBrowserBinding.inflate(layoutInflater)
setContentView(binding.root)
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.zxhy.solarmasterace.manager
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import com.zxhy.solarmasterace.R
import com.zxhy.solarmasterace.databinding.FragmentManagerBinding
import com.zxhy.solarmasterace.quicktools.getMountInfoList
import java.math.BigDecimal
class ManagerFragment : Fragment() {
private lateinit var binding: FragmentManagerBinding
private lateinit var context: Context
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_manager, container, false)
binding = FragmentManagerBinding.bind(root)
return root
}
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
context = requireContext()
val disk = requireContext().getMountInfoList().first()
binding.tvUse.text = "Use:" + disk.getUsedContent(requireContext())
binding.tvFree.text = "Free:" + disk.getFreeContent(requireContext())
val percentF = BigDecimal(disk.used)
.divide(BigDecimal(disk.total), 2, BigDecimal.ROUND_HALF_UP)
val percent = percentF.multiply(BigDecimal(100)).toInt()
binding.tvPercent.text = "$percent%"
binding.cardCategory.setOnClickListener {
categoryMode()
}
binding.cardDocuments.setOnClickListener {
documentsMode()
}
categoryMode()
}
fun categoryMode() {
binding.apply {
val txtColo1 = ContextCompat.getColor(context, R.color.white)
val txtColo2 = ContextCompat.getColor(context, R.color.color_FF5C67E5)
val bgColor2 = ContextCompat.getColor(context, R.color.color_FF4CE0AB)
val bgColor1 = ContextCompat.getColor(context, R.color.white)
cardCategory.setCardBackgroundColor(bgColor2)
ivCategory.setImageResource(R.mipmap.dsa_897)
tvCategory.setTextColor(txtColo1)
cardDocuments.setCardBackgroundColor(bgColor1)
ivDocuments.setImageResource(R.mipmap.wr_5877)
tvDocuments.setTextColor(txtColo2)
clFile.visibility = View.VISIBLE
clDocuments.visibility = View.GONE
}
}
fun documentsMode() {
binding.apply {
val txtColo1 = ContextCompat.getColor(context, R.color.white)
val txtColo2 = ContextCompat.getColor(context, R.color.color_FF5C67E5)
val bgColor2 = ContextCompat.getColor(context, R.color.color_FF4CE0AB)
val bgColor1 = ContextCompat.getColor(context, R.color.white)
cardCategory.setCardBackgroundColor(bgColor1)
ivCategory.setImageResource(R.mipmap.dsa_8972)
tvCategory.setTextColor(txtColo2)
cardDocuments.setCardBackgroundColor(bgColor2)
ivDocuments.setImageResource(R.mipmap.wr_587712)
tvDocuments.setTextColor(txtColo1)
clFile.visibility = View.GONE
clDocuments.visibility = View.VISIBLE
}
}
companion object {
@JvmStatic
fun newInstance() =
ManagerFragment().apply {
arguments = Bundle().apply {}
}
}
}
\ No newline at end of file
package com.zxhy.solarmasterace.permission
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat
import com.zxhy.solarmasterace.dialog.alert
import com.zxhy.solarmasterace.quicktools.ActivityLauncher
import com.zxhy.solarmasterace.quicktools.goToPermissionSettings
fun requestPermission(
context: Context,
activityLauncher: ActivityLauncher,
permission: Array<String>,
tip: String,
agreeAction: (() -> Unit)? = null,
disAgreeAction: (() -> Unit)? = null
) {
val permissionFlag =
permission.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
if (permissionFlag) {
agreeAction?.invoke()
} else {
activityLauncher.launch(permission) { callback ->
val isGrantAll = callback.values.all { it }
if (!isGrantAll) {
context.alert(tip) {
positiveButton("Jump") { dialog ->
goToPermissionSettings(context, activityLauncher)
dialog.dismiss()
}
negativeButton("Cancel") { dialog ->
dialog.dismiss()
disAgreeAction?.invoke()
}
//禁止取消
isCancel(false)
}
} else {
agreeAction?.invoke()
}
}
}
}
\ No newline at end of file
package com.zxhy.solarmasterace.permission
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import com.zxhy.solarmasterace.quicktools.ActivityLauncher
import com.zxhy.solarmasterace.quicktools.settingManageExternalStorage
//Android 13以后,读写权限无法弹出系统弹窗,分化为Manifest.permission.READ_MEDIA_*权限
//Android 11以后,Manifest.permission.READ_EXTERNAL_STORAGE 授予后还需授予 额外储存管理权限 ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
//Android 11以下使用Manifest.permission.READ_EXTERNAL_STORAGE
@SuppressLint("LocalSuppress")
fun Context.requestStoreFollow(
activityLauncher: ActivityLauncher,
disAgreeAction: (() -> Unit)? = null,
agreeAction: (() -> Unit)? = null,
) {
//请求读写管理权限
@SuppressLint("NewApi")
val requestExternalStorageManager: () -> Unit = {
if (!Environment.isExternalStorageManager()) {
val uri = Uri.parse("package:$packageName")
val intent = settingManageExternalStorage(uri)
activityLauncher.launch(intent) {
val flag = Environment.isExternalStorageManager()
if (flag) agreeAction?.invoke() else disAgreeAction?.invoke()
}
} else {
agreeAction?.invoke()
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {//Android 13以后
requestExternalStorageManager()
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {//Android 11以后
requestPermission(
this, activityLauncher,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
), "Set permission to access external storage",
agreeAction = { //读写权限通过,设置储存管理权限
requestExternalStorageManager()
},
disAgreeAction = { disAgreeAction?.invoke() }
)
} else { //Android 11以下,Android 6以上
requestPermission(
this, activityLauncher,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
), "Set permission to access external storage",
agreeAction = {
//读写权限通过
agreeAction?.invoke()
}, disAgreeAction = { disAgreeAction?.invoke() }
)
}
}
package com.zxhy.solarmasterace.quicktools
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultCaller
import androidx.activity.result.contract.ActivityResultContracts
class ActivityLauncher(activityResultCaller: ActivityResultCaller) {
//region 权限
private var permissionCallback: ActivityResultCallback<Map<String, Boolean>>? = null
private val permissionLauncher =
activityResultCaller.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result: Map<String, Boolean> ->
permissionCallback?.onActivityResult(result)
}
fun launch(
permissionArray: Array<String>,
permissionCallback: ActivityResultCallback<Map<String, Boolean>>?
) {
this.permissionCallback = permissionCallback
permissionLauncher.launch(permissionArray)
}
//endregion
//region intent跳转
private var activityResultCallback: ActivityResultCallback<ActivityResult>? = null
private val intentLauncher =
activityResultCaller.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->
activityResultCallback?.onActivityResult(activityResult)
}
/**
* it.resultCode == Activity.RESULT_OK
*/
fun launch(
intent: Intent,
activityResultCallback: ActivityResultCallback<ActivityResult>? = null
) {
this.activityResultCallback = activityResultCallback
intentLauncher.launch(intent)
}
//endregion
}
\ No newline at end of file
package com.zxhy.solarmasterace.quicktools
import android.annotation.SuppressLint
import android.app.usage.StorageStatsManager
import android.content.Context
import android.os.Build
import android.os.storage.StorageManager
import android.os.storage.StorageVolume
import android.text.format.Formatter
import androidx.annotation.RequiresApi
import java.io.File
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.util.UUID
/**
* 计算储存介质
*/
const val TYPE_U = 0 //u盘
const val TYPE_DATA = 1 //data区域
const val TYPE_EXTERNAL = 2 //外置存储
data class Disk(
val path: String = "", //路径
val name: String = "", //名称
val type: Int = 0,//类型 0U盘 1 data区域 2内置存储区域
val total: Long = 0, //总计数量
val used: Long = 0, //已用空间
val free: Long = 0, //空闲空间
) {
fun getTotalContent(context: Context): String {
return Formatter.formatFileSize(context, total)
}
fun getUsedContent(context: Context): String {
return Formatter.formatFileSize(context, used)
}
fun getFreeContent(context: Context): String {
return Formatter.formatFileSize(context, free)
}
}
@SuppressLint("SoonBlockedPrivateApi", "UsableSpace", "ObsoleteSdkInt", "DiscouragedPrivateApi")
fun Context.getMountInfoList(): List<Disk> {
val diskList = arrayListOf<Disk>()
val storageManager = getSystemService(Context.STORAGE_SERVICE) as StorageManager
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {//小于6.0
val getVolumeListMethod = StorageManager::class.java.getDeclaredMethod("getVolumeList")
val volumes = getVolumeListMethod.invoke(storageManager) as Array<StorageVolume>
for (volume in volumes) {
try {
val fileMethod = volume.javaClass.getDeclaredMethod("getPathFile")
val file = fileMethod.invoke(volume) as File
val path = file.path
val name = file.name
val total = file.totalSpace //总计
val used = total - file.freeSpace //空闲
val usable = file.usableSpace //可用
diskList.add(Disk(path, name, -1, total, used, usable))
} catch (e: Exception) {
e.printStackTrace()
}
}
} else {
val getVolumeMethod = StorageManager::class.java.getDeclaredMethod("getVolumes")
val volumes = getVolumeMethod.invoke(storageManager) as List<Any>
for (volume in volumes) {
try {
val getTypeField: Field = volume.javaClass.getField("type")
val type: Int = getTypeField.getInt(volume)
val fileMethod = volume.javaClass.getDeclaredMethod("getPath")
val f: File = fileMethod.invoke(volume) as File
val path = f.path
val name = f.name
val total = if (type != TYPE_U && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val getFsUuid: Method = volume.javaClass.getDeclaredMethod("getFsUuid")
val fsUuid = getFsUuid.invoke(volume) as? String
getTotalSize(fsUuid)
} else {
f.totalSpace //总计
}
val used = total - f.usableSpace //空闲
val usable = f.usableSpace //空闲
val disk = Disk(path, name, type, total, used, usable)
diskList.add(disk)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
return diskList
}
@RequiresApi(Build.VERSION_CODES.O)
fun Context.getTotalSize(fsUuid: String?): Long {
return try {
val id = if (fsUuid == null) StorageManager.UUID_DEFAULT else UUID.fromString(fsUuid)
val stats = getSystemService(StorageStatsManager::class.java)
stats.getTotalBytes(id)
} catch (e: Exception) {
e.printStackTrace()
-1
}
}
\ No newline at end of file
package com.zxhy.solarmasterace.quicktools
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.annotation.RequiresApi
/**
* 访问所有文件权限设置界面
* https://blog.csdn.net/qq_17766199/article/details/115351949
*/
@RequiresApi(Build.VERSION_CODES.R)
fun settingManageExternalStorage(uri: Uri? = null): Intent {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri)
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
return intent
}
//权限设置界面
fun goToPermissionSettings(context: Context, activityLauncher: ActivityLauncher) {
val intent = appSettingsIntent(context.packageName)
activityLauncher.launch(intent)
}
/**
* 应用设置页面意图
*/
fun appSettingsIntent(packageName: String): Intent {
return Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", packageName, null))
}
\ No newline at end of file
package com.zxhy.solarmasterace.quicktools
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.provider.MediaStore
import com.zxhy.solarmasterace.data.FileBean
fun recentImage(context: Context): ArrayList<FileBean> {
val list = arrayListOf<FileBean>()
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))
val photoUri = ContentUris.withAppendedId(uri, id)
list.add(FileBean(uri = photoUri))
if (list.size == 10) {
break
}
} while (cursor.moveToNext())
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
cursor?.close()
}
return list
}
\ No newline at end of file
package com.zxhy.solarmasterace.quicktools
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
/**
* 解析xml布局
*
* @param parent 父布局
* @param attachToRoot 是否依附到父布局
*/
fun Int.inflate(parent: ViewGroup, attachToRoot: Boolean = false): View {
return LayoutInflater.from(parent.context).inflate(this, parent, attachToRoot)
}
package com.zxhy.solarmasterace.tools
import android.annotation.SuppressLint
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView.Adapter
import coil.load
import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import coil.transform.Transformation
import com.zxhy.solarmasterace.R
import com.zxhy.solarmasterace.adapter.AdapterCommonDataFunction
import com.zxhy.solarmasterace.adapter.ItemImage85ViewHolder
import com.zxhy.solarmasterace.data.FileBean
import com.zxhy.solarmasterace.quicktools.inflate
class RecentImageAdapter() : Adapter<ItemImage85ViewHolder>(), AdapterCommonDataFunction {
private val fileList = arrayListOf<FileBean>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemImage85ViewHolder {
return ItemImage85ViewHolder(R.layout.item_card_image_85.inflate(parent))
}
override fun getItemCount(): Int {
return fileList.size
}
override fun onBindViewHolder(holder: ItemImage85ViewHolder, position: Int) {
val data = fileList[position]
holder.binding.apply {
iv.load(data.uri) {
transformations(RoundedCornersTransformation())
}
}
}
@SuppressLint("NotifyDataSetChanged")
override fun setData(data: List<FileBean>) {
fileList.clear()
fileList.addAll(data)
notifyDataSetChanged()
}
}
\ No newline at end of file
package com.zxhy.solarmasterace.tools
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import com.zxhy.solarmasterace.R
import com.zxhy.solarmasterace.databinding.FragmentToolsBinding
import com.zxhy.solarmasterace.quicktools.recentImage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
class ToolsFragment : Fragment() {
private lateinit var binding: FragmentToolsBinding
private lateinit var imageAdapter: RecentImageAdapter
private lateinit var context: Context
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_tools, container, false)
binding = FragmentToolsBinding.bind(root)
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
context = requireContext()
imageAdapter = RecentImageAdapter()
binding.rv.adapter = imageAdapter
loadRecentImage()
}
private fun loadRecentImage() {
lifecycleScope.launch(Dispatchers.IO) {
val list = recentImage(context)
launch(Dispatchers.Main) {
imageAdapter.setData(list)
}
}
}
companion object {
@JvmStatic
fun newInstance() =
ToolsFragment().apply {
arguments = Bundle().apply {
}
}
}
}
\ 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="@mipmap/wuio_8975" android:state_selected="true" />
<item android:drawable="@mipmap/dssd_9895" android:state_selected="false" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FF5C67E5" />
<corners
android:bottomLeftRadius="25dp"
android:bottomRightRadius="25dp" />
</shape>
\ 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="@mipmap/da_878511" android:state_selected="true" />
<item android:drawable="@mipmap/da_8785" android:state_selected="false" />
</selector>
\ 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="@mipmap/sdzs_6897" android:state_selected="true" />
<item android:drawable="@mipmap/fdfas_987" android:state_selected="false" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="-90"
android:endColor="#FF3E48B2"
android:startColor="#FF99A1FF"
android:type="linear" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".filebrowser.FileBrowserActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/cl_arrow"
android:layout_width="60dp"
android:layout_height="36dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:src="@mipmap/df_0011"
tools:ignore="ContentDescription" />
</FrameLayout>
<TextView
android:id="@+id/tv_tittle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Duplicate picture"
android:textSize="17sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<LinearLayout
android:id="@+id/ll_all"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:text="All"
android:textColor="#FF999999"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:src="@drawable/bg_circle_selector"
tools:ignore="ContentDescription" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_tip1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:text="3 items"
android:textColor="#FF666666"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@id/rv"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_top"
tools:ignore="HardcodedText,RtlHardcoded" />
<TextView
android:id="@+id/tv_tip2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:text="Files larger than 10MB"
android:textColor="#FF666666"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@id/rv"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_top"
tools:ignore="HardcodedText,RtlHardcoded" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@id/tv_delete"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_top" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginHorizontal="56dp"
android:layout_marginBottom="54dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container_view"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/b_nav"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/b_nav"
android:layout_marginTop="20dp"
android:background="@color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:itemBackground="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/menu_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
This diff is collapsed.
This diff is collapsed.
<?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="85dp"
android:layout_height="85dp"
android:layout_margin="8dp"
app:cardCornerRadius="8dp"
tools:ignore="ContentDescription">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
<?xml version="1.0" encoding="utf-8"?>
<menu 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">
<item
android:id="@+id/nav_manager"
android:enabled="true"
android:icon="@drawable/bg_manager_selector"
android:title="Manager"
android:visible="true"
app:showAsAction="ifRoom"
tools:ignore="HardcodedText" />
<item
android:id="@+id/nav_tools"
android:enabled="true"
android:icon="@drawable/bg_tools_selector"
android:title="Tools"
android:visible="true"
app:showAsAction="ifRoom"
tools:ignore="HardcodedText" />
</menu>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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