👉 Android Q & Android 11 storage adapter (a) basic knowledge comb

FileOperator

Android file manipulation library. Applicable to Android 4.4 or later, compatible with the new storage policies of AndroidQ and Android11. Including processing Android file directory and cache, file MimeType, file opening method, file path and Uri, file size, file common tool classes, file selection and other functions.

Use (Usage)

1. The dependent (dependencies)

mavenCentral -> Project build.gradle

repositories {
    mavenCentral()
}

implementation 'com. Making. Javakam: file. The core: 1.7.0 @ aar'      // Core Library required
implementation 'com. Making. Javakam: file. The selector: 1.7.0 @ aar'  // File selector
implementation 'com.github.javakam:file.com pressor: 1.7.0 @ aar'// Image compression, the core algorithm is Luban
implementation 'com. Making. Javakam: file. Android - q: 1.7.0 @ aar' / / AndroidQ compatible library, need: 'androidx. Documentfile: documentfile: 1.0.1'
Copy the code

2. ApplicationInitialization in Application

FileOperator.init(this, BuildConfig.DEBUG)
Copy the code

3. The confusion (Proguard)

No reflection, no confusion. (No reflection is used, no need to be confused.)

Preview (Preview)

Function List Cache directory

File Selection

Single Image+Compress Multiple Images +Compress Multiple files+Multiple types

Usage (Usage)

Common File Operations Common File Operations

☘ FileOperator provides some common file manipulation tool classes for Android development. Most of them use static methods. Students who need files can directly CV them

1. Obtain the file MimeType 👉FileMimeType.kt

Obtain the corresponding MimeType according to File Name/Path/Url

fun getMimeType(str: String?).: String {
    ...
}

fun getMimeType(uri: Uri?).: String {
    ...
}

//MimeTypeMap.getSingleton().getMimeTypeFromExtension(...) The supplement
fun getMimeTypeSupplement(fileName: String): String {
    ...
}
Copy the code

2. Calculate the size of a file or folder 👉FileSizeUtils.kt

① Obtain the specifiedFiles/foldersGet the size of the specifiedfile folder)
@Throws(Exception::class)
fun getFolderSize(file: File?).: Long {
    var size = 0L
    if (file == null| |! file.exists())return size
    val files = file.listFiles()
    if (files.isNullOrEmpty()) return size
    for (i in files.indices) {
        size += if (files[i].isDirectory) getFolderSize(files[i]) else getFileSize(files[i])
    }
    return size
}
Copy the code
Get file size
fun getFileSize(file: File?).: Long{... }fun getFileSize(uri: Uri?).: Long{... }Copy the code
③ Automatic calculation of the specifiedFiles/foldersAutomatically calculate the size of the specifiedfile folder)

Automatically calculates the size of a specified file or folder. The returned value is a character string in units of B, KB, M, GB, or TB

fun getFileOrDirSizeFormatted(path: String?).: String {}... }Copy the code
④ Formatting size (BigDecimalImplementation)

Format size (implemented by Big Decimal)

/ * * *@paramAccurate to a few decimal places */
fun formatFileSize(size: Long, scale: Int, withUnit: Boolean = false): String {
    val divisor = 1024L
    //ROUND_DOWN 1023 -> 1023B ; ROUND_HALF_UP 1023 -> 1KB
    val kiloByte: BigDecimal =
        formatSizeByTypeWithDivisor(BigDecimal.valueOf(size), scale, SIZE_TYPE_B, divisor)
    if (kiloByte.toDouble() < 1) {
        return "${kiloByte.toPlainString()}${if (withUnit) SIZE_TYPE_B.unit else ""}"
    }
    //KB
    val megaByte = formatSizeByTypeWithDivisor(kiloByte, scale, SIZE_TYPE_KB, divisor)
    if (megaByte.toDouble() < 1) {
        return "${kiloByte.toPlainString()}${if (withUnit) SIZE_TYPE_KB.unit else ""}"
    }
    //M
    val gigaByte = formatSizeByTypeWithDivisor(megaByte, scale, SIZE_TYPE_MB, divisor)
    if (gigaByte.toDouble() < 1) {
        return "${megaByte.toPlainString()}${if (withUnit) SIZE_TYPE_MB.unit else ""}"
    }
    //GB
    val teraBytes = formatSizeByTypeWithDivisor(gigaByte, scale, SIZE_TYPE_GB, divisor)
    if (teraBytes.toDouble() < 1) {
        return "${gigaByte.toPlainString()}${if (withUnit) SIZE_TYPE_GB.unit else ""}"
    }
    //TB
    return "${teraBytes.toPlainString()}${if (withUnit) SIZE_TYPE_TB.unit else ""}"
}
Copy the code

Convert file size specify the type of conversion (Convert file size):

// Scale is accurate to a few decimal places
fun formatSizeByTypeWithoutUnit(size: BigDecimal, scale: Int, sizeType: FileSizeType): BigDecimal =
    size.divide(
        BigDecimal.valueOf(when (sizeType) {
            SIZE_TYPE_B -> 1L
            SIZE_TYPE_KB -> 1024L
            SIZE_TYPE_MB -> 1024L * 1024L
            SIZE_TYPE_GB -> 1024L * 1024L * 1024L
            SIZE_TYPE_TB -> 1024L * 1024L * 1024L * 1024L
        }),
        scale,
        //ROUND_DOWN 1023 -> 1023B ; ROUND_HALF_UP 1023 -> 1KB
        if (sizeType == SIZE_TYPE_B) BigDecimal.ROUND_DOWN else BigDecimal.ROUND_HALF_UP
    )
Copy the code

Convert file size with unit:

fun formatSizeByTypeWithUnit(size: Long, scale: Int, sizeType: FileSizeType): String {
    return "${ formatSizeByTypeWithoutUnit(size.toBigDecimal(), scale, sizeType).toPlainString() }${sizeType.unit}"
}
Copy the code

3. Open the Url/Uri(remote or local)👉FileOpener.kt

① Open the System Sharing popup
fun openShare(context: Context, uri: Uri, title: String = "Share files") {
    val intent = Intent(Intent.ACTION_SEND)
    intent.putExtra(Intent.EXTRA_STREAM, uri)
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    // Put the Uri and MIME type in the result Intent
    intent.setDataAndType(uri, getMimeType(uri))
    context.startActivity(Intent.createChooser(intent, title))
}
Copy the code
② Open browser
@SuppressLint("QueryPermissionsNeeded")
fun openBrowser(
    context: Context, url: String, title: String = "Please select a browser", newTask: Boolean = false,
    block: ((result: Boolean.msg: String?). ->Unit)? = null.) {
    try {
        val intent = Intent(Intent.ACTION_VIEW)
        intent.data = Uri.parse(url)
        if (newTask) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        //startActivity(intent)
        //https://developer.android.com/about/versions/11/privacy/package-visibility
        if(intent.resolveActivity(context.packageManager) ! =null) { context.startActivity(Intent.createChooser(intent, title)) block? .invoke(true.null)}else{ block? .invoke(true."No browser available")}}catch(e: ActivityNotFoundException) { e.printStackTrace() block? .invoke(true, e.toString())
    }
}
Copy the code
③ Open it directlyUrlCorresponding system application

Directly open the system application corresponding to Url

Eg: If the URL is a video address, the system will open it directly with the built-in video player

fun openUrl(activity: Activity, url: String?). {
    try {
        val intent = Intent(Intent.ACTION_VIEW)
        intent.setDataAndType(Uri.parse(url), getMimeType(url))
        activity.startActivity(intent)
    } catch (e: Exception) {
        FileLogger.e("openUrl error : " + e.message)
    }
}
Copy the code
(4) according toThe file pathandType (suffix judgment)Displays programs that support the format

According to file path and type (judgment by suffix) show programs that support the format

fun openFile(context: Any, uri: Uri? , mimeType:String? = null)= uri? .let { u -> Intent.createChooser(createOpenFileIntent(u, mimeType),"Selection program")? .let { startActivity(context, it) } }Copy the code

4. Obtain the file Uri/Path👉FileUri.kt

(1) from theFileGet from pathUri

Obtain Uri from File path

fun getUriByPath(path: String?).: Uri? = if (path.isNullOrBlank()) null else getUriByFile(File(path))

fun getUriByFile(file: File?).: Uri? = file? .let {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val authority = FileOperator.getContext().packageName + PATH_SUFFIX
            FileProvider.getUriForFile(FileOperator.getContext(), authority, file)
        } else Uri.fromFile(file)
    }
Copy the code
(2) to obtainUriThe corresponding file path is compatibleAPI 26

Get the file path corresponding to Uri, compatible with API 26

fun getFilePathByUri(context: Context? , uri:Uri?).: String? {
    if (context == null || uri == null) return null
    val scheme = uri.scheme
    // open using third-party applications starting with file://
    if (ContentResolver.SCHEME_FILE.equals(scheme, ignoreCase = true)) return uri.path
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) getPath(context,
        uri) else getPathKitkat(context, uri)
}
Copy the code

5. General file tools 👉FileUtils.kt

Method Remark
getMediaShotTime(uri: Uri? , block: (Long)) Get the shooting time of media files
formatMediaMetadataKeyDate(date: String?) : Date? conversionMediaMetadataRetriever.METADATA_KEY_DATESpecial time format
dumpMediaInfoByMediaMetadataRetriever(uri) printAudio or videoDetails of(Use MediaMetadataRetriever)
dumpMediaInfoByExifInterface(uri) printThe pictureDetails of(Use ExifInterface)
checkImage(uri) checkUriWhether the corresponding file isThe picture
checkRight(uri) checkUriWhether it is correct;UriWhether the file pointing to exists
getExtension Get file suffixesjpg
getExtensionFull Get the full file suffix.jpg
splitFilePath() Split file path eg:/xxx/xxx/note.txt 👉 path: /xxx/xxx(Note: Not at the tail/) name:note suffix: txt
getFileNameFromPath(path: String?) throughFilePathGet file name
getFileNameFromUri(uri: Uri?) throughUriGet file name
createFile(filePath: String? , fileName: String? , overwrite: Boolean = false):File? Note (1).txt is generated when note. TXT already exists. Note (1).txt is generated when note
createDirectory(filePath: String?) : Boolean Create a directory
deleteFile Delete a file or directory
deleteFileWithoutExcludeNames(file: File? , vararg excludeDirs: String?) Delete files or directories,excludeDirsSome of the names specifiedFiles/foldersDo not delete
deleteFilesNotDir Delete only files, not folders
readFileText(InputStream/Uri): String? Read the contents of a text file
readFileBytes(InputStream/Uri): ByteArray? Reads the contents of the file and returnsByteArray
copyFile Copy files according to the file pathjava.nio
writeBytes2File(bytes: ByteArray, target: File) theByteArrayWrite to target filetarget(File)In the
write2File(bitmap:Bitmap, file:File? , overwrite:Boolean=false) theBitmapWrite to file, passBitmapFactory.decodeStream()Read out
write2File(input:InputStream? , file:File? , overwrite:Boolean=false) Writes data to a file
isLocal Verify that it is a local URI
isGif() Verify that it is a GIF

CopyFile efficiency is similar to kotlin.io. Fileskt__utilskt. copyTo in kotlin-stdlib-1.4.21.jar:

fun File.copyTo(
    target: File,
    overwrite: Boolean = false,
    bufferSize: Int = DEFAULT_BUFFER_SIZE
): File
Copy the code

Usage:

boolean copyResult = FileUtils . copyFile (fileOld, getExternalFilesDir(null).getPath(), "test.txt");
File targetFile = new File(getExternalFilesDir(null).getPath() + "/" + "test.txt");
Copy the code

Select File from ‘Select File’

Implementation ‘com. Making. Javakam: file selector: X.X.X @ aar’ / / file selector (file selector)

1. Single Selection picture

val optionsImage = FileSelectOptions().apply {
    fileType = FileType.IMAGE
    fileTypeMismatchTip = "File type mismatch!" //File type mismatch
    singleFileMaxSize = 5242880
    singleFileMaxSizeTip = "Maximum image size is 5M!" //The largest picture does not exceed 5M
    allFilesMaxSize = 10485760
    allFilesMaxSizeTip =
        "The total image size does not exceed 10M!"//The total picture size does not exceed 10M
    fileCondition = object : FileSelectCondition {
        override fun accept(fileType: IFileType, uri: Uri?).: Boolean {
            return(fileType == FileType.IMAGE && uri ! =null&&! uri.path.isNullOrBlank() && ! FileUtils.isGif( uri)) } } } mFileSelector = FileSelector .with(this)
    .setRequestCode(REQUEST_CHOOSE_FILE)
    .setTypeMismatchTip("File type mismatch!") //File type mismatch
    .setMinCount(1."Select at least one file!) //Choose at least one file
    .setMaxCount(10."Select up to ten files!") //Choose up to ten files //Choose up to ten files
    .setOverLimitStrategy(OVER_LIMIT_EXCEPT_OVERFLOW)
    .setSingleFileMaxSize(1048576."No more than 1M in size!") / / The size always exceed 1 m note: radio is invalid under The condition, FileSelectOptions. SingleFileMaxSize
    .setAllFilesMaxSize(10485760."The total size cannot exceed 10M!") //The total size cannot exceed 10M note: setSingleFileMaxSize cannot exceed 10M
    .setMimeTypes("image/*") / / the default do not file type constraints for "* / *", the choice of different types of the system to provide the UI is not the same as eg: "video / *", "audio / *", "image / *"
    .applyOptions(optionsImage)
    .filter(object : FileSelectCondition {
        override fun accept(fileType: IFileType, uri: Uri?).: Boolean {
            return when(fileType) { FileType.IMAGE -> (uri ! =null&&! uri.path.isNullOrBlank() && ! FileUtils.isGif(uri)) FileType.VIDEO ->false
                FileType.AUDIO -> false
                else -> false
            }
        }
    })
    .callback(object : FileSelectCallBack {
        override fun onSuccess(results: List<FileSelectResult>? {
            showSelectResult(results)
        }
        override fun onError(e: Throwable?). {
            FileLogger.e("FileSelectCallBack onError ${e? .message}")
        }
    })
    .choose()
Copy the code

🍎 If multiple images (multiple + single type)

🍎 If it is a multi-select image (multiple selection + single type)

val optionsImage = FileSelectOptions().apply {
    fileType = FileType.IMAGE
    / /...
}
mFileSelector = FileSelector.with(this)
    .setMultiSelect() EN: Enable multiple Selection (default single selection)
    / /...
    .choose()
Copy the code

2. Multiple files (multiple + multiple types)

Multiple files (multi-select multiple types)

🌴 is suitable for complex file selection, for example, selecting images, audio files, and text files. Select at least one image and at most two images. The size of each image does not exceed 5M, and the size of all images does not exceed 10M.

You can select at least two or a maximum of three audio files. The size of each audio file cannot exceed 20M, and the size of all audio files cannot exceed 30M. You can select at least one text file and at most two. The size of each text file cannot exceed 5M, and the size of all text files cannot exceed 10M

🌴It is suitable for processing complex file selection situations, such as: select pictures, audio files, text files, among which, select at least one picture and two at most. The size of each picture does not exceed 5M, and the size of all pictures does not exceed 10M; audio File Choose at least two and a maximum of three, each audio size does not exceed 20M, all audio size does not exceed 30M; text file select at least one, select at most two, each text file size does not exceed 5M, all The text file size does not exceed 10M

/ / picture Image
val optionsImage = FileSelectOptions().apply {
    fileType = FileType.IMAGE
    minCount = 1
    maxCount = 2
    minCountTip = "Choose at least one picture." //Select at least one picture
    maxCountTip = "Select a maximum of two images" //Select up to two pictures
    singleFileMaxSize = 5242880
    singleFileMaxSizeTip = "Maximum single picture is 5M!" //A single picture does not exceed 5M !
    allFilesMaxSize = 10485760
    allFilesMaxSizeTip = "The total image size does not exceed 10M!" //The total size of the picture does not exceed 10M !
    fileCondition = object : FileSelectCondition {
        override fun accept(fileType: IFileType, uri: Uri?).: Boolean {
            return(fileType == FileType.IMAGE && uri ! =null&&! uri.path.isNullOrBlank() && ! FileUtils.isGif( uri)) } } }/ / Audio Audio
val optionsAudio = FileSelectOptions().apply {
    fileType = FileType.AUDIO
    minCount = 2
    maxCount = 3
    minCountTip = "Select at least two audio files" //Select at least two audio files
    maxCountTip = "Select up to three audio files" //Select up to three audio files
    singleFileMaxSize = 20971520
    singleFileMaxSizeTip = "Single audio Max 20M!" //Maximum single audio does not exceed 20M !
    allFilesMaxSize = 31457280
    allFilesMaxSizeTip = "The total audio size does not exceed 30M!" //The total audio size does not exceed 30M !
    fileCondition = object : FileSelectCondition {
        override fun accept(fileType: IFileType, uri: Uri?).: Boolean {
            return(uri ! =null)}}}// Text file TXT
val optionsTxt = FileSelectOptions().apply {
    fileType = FileType.TXT
    minCount = 1
    maxCount = 2
    minCountTip = "Select at least one text file" //Select at least one text file
    maxCountTip = "Select a maximum of two text files" //Select at most two text files
    singleFileMaxSize = 5242880
    singleFileMaxSizeTip = "The maximum size of a single text file is 5M!" //The single biggest text file no more than 5M
    allFilesMaxSize = 10485760
    allFilesMaxSizeTip = "The total size of text files does not exceed 10M!" //Total size not more than 10M text file
    fileCondition = object : FileSelectCondition {
        override fun accept(fileType: IFileType, uri: Uri?).: Boolean {
            return(uri ! =null)}}}/* Note: FileSelectOptions do not return eg if they fail to pass the qualifiers: If the minimum number of audio files is set to '2', all audio selection results will be removed. Therefore, only images and text files (limited to OVER_LIMIT_EXCEPT_OVERFLOW) are returned. if a FileSelectOptions failed the qualification, then the FileSelectOptions will not return, Eg: using the restriction conditions, images, audio, text files, each choose a, because audio set the minimum amount as ` 2 ` set does not meet the conditions the choice of the results to remove all audio , Only pictures and text files, so return result (limited to OVER_LIMIT_EXCEPT_OVERFLOW); * /
mFileSelector = FileSelector
    .with(this)
    .setRequestCode(REQUEST_CHOOSE_FILE)
    .setMultiSelect() // The default is radio false

    /* The actual minimum number is setMinCount and (optionSimage.mincount + optionsAudio. MinCount +...). The actual maximum number of minimum values in setMaxCount and (optionSimage.maxCount + optionsAudio. MaxCount +...) EN: Actual minimum limit for setMinCount and (optionsImage minCount optionsAudio. MinCount... The lowest value of), Actual maximum limit for setMaxCount and (optionsImage maxCount optionsAudio. MaxCount... ) the maximum, so the maximum limit here is invalid; * /
    .setMinCount(1."Select at least one type file!") //Select at least one set type file
    .setMaxCount(4."Select up to four files!") //Most alternative four files

    / * actual single file size limit for setSingleFileMaxSize and (optionsImage. SingleFileMaxSize + optionsAudio. SingleFileMaxSize +...). The minimum value Actual total size limit for setAllFilesMaxSize and (optionsImage. AllFilesMaxSize + optionsAudio. AllFilesMaxSize +...). Maximum EN: Actual single file size limit for setSingleFileMaxSize and (optionsImage. SingleFileMaxSize optionsAudio. SingleFileMaxSize... The lowest value of), Actual total size limit for setAllFilesMaxSize and (optionsImage allFilesMaxSize optionsAudio. AllFilesMaxSize... The highest value in); * /
    // The single file size limit set in 'custom FileSelectOptions' is preferred. If not, use this value
    //EN:Prefer using ` custom FileSelectOptions ` set in single file size limit, if the value is not set is used
    .setSingleFileMaxSize(2097152."Single file size cannot exceed 2M!") //The size of a single file cannot exceed 2M !
    .setAllFilesMaxSize(52428800."The total file size cannot exceed 50M!") //The total file size cannot exceed 50M !

    //1. The number or size of files exceeds the limit
    //2. Single type: keep files that do not exceed the limit and return them. Multiple types: Keep the correct files and remove all files of the wrong type
    //EN:
    //1. Documents more than limit or size limit
    //2. Single type: keep not ultra limit file and return, get rid of the overflow part; Multiple types: keep the right file, get rid of the wrong type of all documents
    .setOverLimitStrategy(this.mOverLimitStrategy)

    //eg: ando.file.core.FileMimeType
    / / the default do not file type constraints for "* / *", the choice of different types of the system to provide the UI is not the same as eg: "video / *", "audio / *", "image / *"
    //EN:Default do not file type constraints for "/", is not the same as the choice of different types of the system to provide the UI eg: "video/"," audio/", "image/"
    .setMimeTypes("audio/*"."image/*"."text/plain")

    // File type mismatch occurs if setMimeTypes and applyOptions do not correspond
    //EN:If setMimeTypes and applyOptions no corresponding will appear `file type mismatch problems`
    .applyOptions(optionsImage, optionsAudio, optionsTxt)

    // Use FileSelectCondition set in FileSelectOptions first
    //EN:Priority in use FileSelectOptions FileSelectCondition Settings
    .filter(object : FileSelectCondition {
        override fun accept(fileType: IFileType, uri: Uri?).: Boolean {
            return when(fileType) { FileType.IMAGE -> (uri ! =null&&! uri.path.isNullOrBlank() && ! FileUtils.isGif(uri)) FileType.AUDIO ->true
                FileType.TXT -> true
                else -> false
            }
        }
    })
    .callback(object : FileSelectCallBack {
        override fun onSuccess(results: List<FileSelectResult>? {
            showSelectResult(results)
        }
        override fun onError(e: Throwable?). {
            FileLogger.e("FileSelectCallBack onError ${e? .message}")
        }
    })
    .choose()
Copy the code

3. Customize FileType(Custom FileType)

① Extend the existing FileType

Extend existing FileType

Eg: built-in (builtin): TXT(mutableListOf("txt"."conf"."iml"."ini"."log"."prop"."rc"Increase: filetype.txt.supplement ("gradle"."kt"Result: TXT(mutableListOf)"txt"."conf"."iml"."ini"."log"."prop"."rc"."gradle"."kt"Remove: filetype.txt.remove ()"txt"."ini"Result: TXT(mutableListOf)"conf"."iml", log"," prop "," rc ")) replace (replace): filetype.xml.replace ("xxx") debugging: filetype.txt.dump ()Copy the code
(2) byIFileTypeUser-defined file types

Through IFileType custom file type

🍎 provides two ways to do this:

/ / 1. The way a
object FileTypePhp : IFileType {
    override fun fromUri(uri: Uri?).: IFileType {
        return if (parseSuffix(uri).equals("php".true)) FileTypePhp else FileType.UNKNOWN
    }
}

//2. Recommended Way
enum class FileTypeJson : IFileType {
    JSON;

    override fun fromUri(uri: Uri?).: IFileType {
        return resolveFileMatch(uri, "json", JSON)
    }
}
Copy the code

Usage (Usage) :

val optionsJsonFile = FileSelectOptions().apply {
    fileType = FileTypeJson.JSON
    minCount = 1
    maxCount = 2
    minCountTip = "Select at least one JSON file" //Choose at least one JSON file
    maxCountTip = "Select a maximum of two JSON files" //Choose up to two JSON files
}

FileSelector.with(this)... .setMimeTypes("audio/*"."image/*"."text/*"."application/json")
    .applyOptions(optionsImage, optionsAudio, optionsTxt, optionsJsonFile)
    .filter(object : FileSelectCondition {
        override fun accept(fileType: IFileType, uri: Uri?).: Boolean {
            return when(fileType) { FileType.IMAGE -> (uri ! =null&&! uri.path.isNullOrBlank() && ! FileUtils.isGif(uri)) FileType.AUDIO ->true
                FileType.TXT -> true
                FileTypeJson.JSON -> true
                else -> false
            }
        }
    })
    .choose()
Copy the code

Note: JSON files cannot be opened with text/*. The corresponding mimeType is Application /json

3. Image CompressionImageCompressor.kt

1. Direct compression without caching

val bitmap: Bitmap = ImageCompressEngine.compressPure(uri)
Copy the code

Compress pictures and cache

1.Luban algorithm; 2. Direct compression - > val bitmap:Bitmap=ImageCompressEngine.com pressPure (uri) * * T 👉 filePath/uri/File * /
fun <T> compressImage(context: Context, photos: List<T>, success: (index: Int.uri: Uri?). ->Unit) {
    ImageCompressor
        .with(context)
        .load(photos)
        .ignoreBy(100)//Byte
        .setTargetDir(getCompressedImageCacheDir())
        .setFocusAlpha(false)
        .enableCache(true)
        .filter(object : ImageCompressPredicate {
            override fun apply(uri: Uri?).: Boolean {
                //FileLogger.i("compressImage predicate $uri ${FileUri.getFilePathByUri(uri)}")
                return if(uri ! =null) !FileUtils.getExtension(uri).endsWith("gif") else false
            }
        })
        .setRenameListener(object : OnImageRenameListener {
            override fun rename(uri: Uri?).: String {
                try {
                    val filePath = FileUri.getFilePathByUri(uri)
                    val md = MessageDigest.getInstance("MD5") md.update(filePath? .toByteArray() ? :return "")
                    return BigInteger(1, md.digest()).toString(32)}catch (e: NoSuchAlgorithmException) {
                    e.printStackTrace()
                }
                return ""
            }
        })
        .setImageCompressListener(object : OnImageCompressListener {
            override fun onStart(a) {}
            override fun onSuccess(index: Int, uri: Uri?). {
                success.invoke(index, uri)
            }

            override fun onError(e: Throwable?). {
                FileLogger.e("OnImageCompressListener onError ${e? .message}")
            }
        }).launch()
}
Copy the code

Summary (Summary)

  1. onActivityResultTo submit the results of the selection fileFileSelectorProcessing:
override fun onActivityResult(requestCode: Int, resultCode: Int.data: Intent?). {
    super.onActivityResult(requestCode, resultCode, data)

    // Select results to FileSelector, which can be distinguished by 'requestCode -> REQUEST_CHOOSE_FILE'mFileSelector? .obtainResult(requestCode, resultCode,data)}Copy the code
  1. There are two strategies for selecting files that do not meet the preset conditions:

    • OVER_LIMIT_EXCEPT_ALL An error message is returned when the number or size of a file exceeds the threshold

    • OVER_LIMIT_EXCEPT_OVERFLOW ① The number or size of files exceeds the upper limit. (2) Single type: keep the files not exceeding the limit and return them, remove the overflow part behind; Multiple types: keep the correct files and remove all files of the wrong type; (3) onSuccess callback

  2. Select file data: singleintent. GetData; Multi-select Intent. GetClipData

  3. Intent.putextra (Intent.extra_allow_multiple, true) selects only one file in an Intent. ︴ Σ (° delta ° | | |)

  4. The corresponding directory is automatically created when the Activity executes getExternalFilesDirs(environment.directory_xxx) and other methods to obtain directory addresses

  1. Uri.fromfile (file) generated file:///… Therefore, you need to use the FileProvider to share files in the App Specific directory to other apps. You need to use the FileProvider to resolve the path that can be shared: ando.file.core.FileUri.getUriByFile(file)

  2. FileOperatorQ Usage: github.com/javakam/Fil…


Q&A

README_QA. Md github.com/javakam/Fil…

Update log

README_VERSIONS. Md github.com/javakam/Fil…

Thank you (Thanks)

README_THANKS. Md github.com/javakam/Fil…

LICENSE (LICENSE)

Copyright 2019 javakam, FileOperator Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Copy the code