FileOperator Android Library

repository·master·Indexed 23 days ago

https://github.com/javakam/fileoperator

An Android file operation library compatible with Android 4.4 and above. It provides utilities for directory and cache management, MIME type handling, file size calculation, and path/URI resolution. The library includes a core module, a FileSelector for constrained file picking, and an ImageCompressor based on the Luban algorithm for image optimization.

Tokens
5.5K
Snippets
8
Records
32
Agent score
81%

What's inside FileOperator

  1. Configure overflow strategies for file selection limits

    master

    When file selection exceeds preset constraints (such as maximum quantity or maximum file size), you can choose between two strategies:

    1. OVER_LIMIT_EXCEPT_ALL: If the limits are exceeded, the operation fails immediately and triggers the onError callback.
    2. OVER_LIMIT_EXCEPT_OVERFLOW: If limits are exceeded, the system attempts to recover:
      • If the limit is exceeded by quantity or size: It keeps the files that do not exceed the limits and discards the overflowing ones.
      • If the limit is exceeded by type: It keeps the correct file types and discards all files of the incorrect type.
      • This strategy triggers the onSuccess callback with the valid subset of files.
  2. Install FileOperator via Gradle

    master

    To use FileOperator, add mavenCentral() and the Sonatype public repository to your project's build.gradle. You can then include the core library, the file selector, and the image compressor. Note that the core library is required for all features.

    repositories {
       mavenCentral()
       maven { url "https://s01.oss.sonatype.org/content/groups/public" }
    }
    
    implementation 'com.github.javakam:file.core:3.9.8@aar'      //核心库必选(Core library required)
    implementation 'com.github.javakam:file.selector:3.9.8@aar'  //文件选择器(File selector)
    implementation 'com.github.javakam:file.compressor:3.9.8@aar'//图片压缩,修改自Luban(Image compression, based on Luban)
  3. Handle file selection results in onActivityResult

    master

    To process the results of a file selection request, you must pass the onActivityResult parameters to the FileSelector instance. It is recommended to use a specific requestCode (e.g., REQUEST_CHOOSE_FILE) to distinguish file selection results from other activity results.

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
       super.onActivityResult(requestCode, resultCode, data)
    
       // Pass the selection result to FileSelector, distinguishing via requestCode
       mFileSelector?.obtainResult(requestCode, resultCode, data)
    }
  4. Select Files with FileSelector

    master

    The FileSelector library (available as an AAR) allows for sophisticated file picking with constraints on type, count, and size.

    Single Selection (e.g., Images)

    Configure FileSelectOptions to define constraints like fileType, singleFileMaxSize, and allFilesMaxSize. Use .filter() to implement custom logic (e.g., excluding GIFs).

    Multi-Selection (Multiple Types)

    To select different types of files simultaneously (e.g., 1-2 images AND 2-3 audio files), use .setMultiSelect() and .applyOptions(...) with multiple FileSelectOptions objects.

    Important Strategy: When using multi-selection, it is recommended to use .setOverLimitStrategy(OVER_LIMIT_EXCEPT_OVERFLOW). This ensures that if one type fails to meet its minimum count requirement, it is simply omitted from the results rather than causing the entire selection process to fail.

    val optionsImage = FileSelectOptions().apply {
       fileType = FileType.IMAGE
       minCount = 1
       maxCount = 2
       singleFileMaxSize = 5242880
       allFilesMaxSize = 10485760
       fileCondition = object : FileSelectCondition {
          override fun accept(fileType: IFileType, uri: Uri?): Boolean {
             return (fileType == FileType.IMAGE && uri != null && !FileUtils.isGif(uri))
          }
       }
    }
    
    FileSelector.with(this)
       .setMultiSelect()
       .setRequestCode(REQUEST_CHOOSE_FILE)
       .setOverLimitStrategy(OVER_LIMIT_EXCEPT_OVERFLOW)
       .applyOptions(optionsImage, optionsAudio, optionsTxt)
       .callback(object : FileSelectCallBack {
          override fun onSuccess(results: List<FileSelectResult>?) { /* handle success */ }
          override fun onError(e: Throwable?) { /* handle error */ }
       })
       .choose()
  5. Initialize FileOperator in your Application

    master

    Call FileOperator.init within your Application class to set up the library. Pass the application context and a boolean indicating if the app is in debug mode (e.g., BuildConfig.DEBUG).

    FileOperator.init(this, BuildConfig.DEBUG)
  6. Compress images using ImageCompressor

    master

    The ImageCompressor class provides a builder-based API to compress images asynchronously or synchronously. You can load images via String paths, File objects, or Uri objects.

    Asynchronous Compression

    Use .launch() to start a background compression process. You must provide an OnImageCompressListener to receive progress and results.

    Synchronous Compression

    Use .get() to perform compression on the current thread and return a list of compressed Uri objects.

  7. Handle FileSelector results in Activity or Fragment

    master

    After launching the file picker, you must pass the results from onActivityResult (or the equivalent ActivityResult callback) back to the FileSelector instance using obtainResult to trigger the configured FileSelectCallBack.

    // In your Activity or Fragment
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        // 'fileSelector' is the instance created via FileSelector.with(...)
        fileSelector.obtainResult(requestCode, resultCode, data)
    }
  8. Initialize FileSelector using the Builder pattern

    master

    To use FileSelector, start by calling FileSelector.with(context) or FileSelector.with(fragment). This returns a Builder instance used to configure selection constraints such as file types, counts, and size limits. Once configured, call .choose() to launch the file picker.

    Note: If you are using modern Android ActivityResultLauncher, pass it to the with method to ensure compatibility with the new API.

  9. Fix Invalid image: ExifInterface unsupported format

    master

    If you encounter W / ExifInterface: Invalid image : ExifInterface got an unsupported image format, it is likely due to using the platform's default android.media.ExifInterface.

    To fix this, use the AndroidX ExifInterface library which supports a wider range of formats and handles corrupted files more gracefully.

    1. Add the dependency to your build.gradle:
    2. Replace all imports of android.media.ExifInterface with androidx.exifinterface.media.ExifInterface.
    dependencies {
        compileOnly "androidx.exifinterface:exifinterface:1.3.2"
        ...
    }
  10. Ensure consistent requestCode in FileSelector

    master

    When using FileSelector, you must ensure that the requestCode used in setRequestCode() matches the requestCode received in obtainResult().

    Starting from version v3.9.8, if these values do not match, the library will trigger an error callback via mFileSelectCallBack?.onError() with the following error message:

    "请比较 setRequestCode() 和 obtainResult() 方法中的 requestCode 值是否一致!(Please compare whether the requestCode values in setRequestCode() and obtainResult() methods are consistent!)"

    Note that the default requestCode for FileSelector is 1 (val REQUEST_CODE_DEFAULT: Int = 1).

  11. Fix ActivityNotFoundException for Intent actions

    master
    If you encounter android.content.ActivityNotFoundException when attempting to open documents, ensure that your Intent configuration is compatible. Specifically, avoid conflicting MIME type settings in ando.file.core.FileOpener.createChooseIntent by ensuring the type and extra MIME types are consistent (e.g., don't set type to image/* while providing audio/* in extras).