GithubHelp home page GithubHelp logo

rosuh / androidfilepicker Goto Github PK

View Code? Open in Web Editor NEW
937.0 937.0 84.0 1.64 MB

FilePicker is a small and fast file selector library that is constantly evolving with the goal of rapid integration, high customization, and configurability~

Home Page: https://afp.rosuh.me

License: MIT License

Java 1.00% Kotlin 99.00%
android file-browser file-picker

androidfilepicker's Introduction

1️⃣ 🧑‍💻 Full time Mobile Developer, most focus on Android

2️⃣ ▶ Responsible for live Streaming-Related business.

3️⃣ ⛏️ Digging into Kotlin Multiplatform now ;)

4️⃣ Chinese / Enghlish, Currently working in Shenzhen, China. Open to relocation

📫 Email, Telegram, Home Page.

androidfilepicker's People

Contributors

dependabot-preview[bot] avatar rosuh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

androidfilepicker's Issues

doesn't work

doesn't work on Samsung SM-G9550 Android 8.0.0 API 26

在Fragment中使用空指针

/**
* 不能使用 fragment.getContext(),因为无法保证外部的代码环境
*/
fun from(fragment: Fragment):FilePickerConfig{
this.fragment = WeakReference(fragment)
this.context = WeakReference(fragment.activity!!)
return config!!
}
在fragment 中 FilePickerConfig 是不是没有初始化?还是我使用方法不对。

可否添加选择文件夹的功能?

既然名字叫FilePicker,而实际上 Directory 也是 File,所以添加选择文件夹的功能也是很正常的吧?

以上都是胡扯,就是我需要这个功能(单选/多选文件夹)……

External Storage Supported (Sd-card, other externals ...)

Hi,
Thank you very much for your contributions. I have to use your library with many customize and this is some point that I have made a change to my own.

This is some sample code that anyone can try

  1. FileUtils
        /**
         * 根据配置参数获取根目录文件
         * @return File
         */
        fun getRootFile(): File {
            return when (FilePickerManager.config.mediaStorageType) {
                STORAGE_EXTERNAL_STORAGE -> {
                    File(Environment.getExternalStorageDirectory().absoluteFile.toURI())
                }
                STORAGE_CUSTOM_ROOT_PATH -> {
                    if (FilePickerManager.config.customRootPath.isEmpty()) {
                        File(Environment.getExternalStorageDirectory().absoluteFile.toURI())
                    } else {
                        File(FilePickerManager.config.customRootPath)
                    }
                }
                else -> {
                    File(Environment.getExternalStorageDirectory().absoluteFile.toURI())
                }
            }
        }

        /**
         * Function to get all external storage list
         */
        fun getExternalStorageList(context: Context): List<String> {
            val files = ContextCompat.getExternalFilesDirs(context, null)
            val externalFiles = mutableListOf<String>()
            for (i in files.indices) {
                val file = files[i]
                if (file != null && file.canRead()) {
                    val substring = file.absolutePath
                        .substring(0, file.absolutePath.indexOf("/Android/data/"))
                    externalFiles.add(substring)
                }
            }
            return externalFiles
        }
  1. Change when user tap to on item of Nav Header, then if they tap to first position (like root of the path), We will display the popup menu
    override fun onItemClick(
        recyclerAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>,
        view: View,
        position: Int
    ) {
        val item = (recyclerAdapter as me.rosuh.filepicker.adapter.BaseAdapter).getItem(position)
        item ?: return
        val file = File(item.filePath)
        if (!file.exists()) {
            return
        }
        when (view.id) {
            R.id.item_list_file_picker -> {
                if (file.isDirectory) {
                    (rv_nav_file_picker?.adapter as? FileNavAdapter)?.let {
                        saveCurrPos(it.data.last(), position)
                    }
                    // 如果是文件夹,则进入
                    enterDirAndUpdateUI(item)
                } else {
                    // Navigate data to activity
                    FilePickerManager.config.fileItemOnClickListener.onItemClick(
                        recyclerAdapter,
                        view,
                        position
                    )
                }
            }
            R.id.item_nav_file_picker -> {
                if (file.isDirectory) {
                    // In-case this is the root of path
                    if (position == 0 && strExternalStoragePaths.size > 1) {
                        // Display the popup that the user can switch between external storage like SD-card, internal storage ...
                        /* Creating the instance of PopupMenu */
                        popup = PopupMenu(
                            requireContext(),
                            (rv_nav_file_picker?.layoutManager as? LinearLayoutManager)?.findViewByPosition(
                                0
                            ) ?: rv_nav_file_picker
                        )

                        for (index in strExternalStoragePaths.indices) {
                            popup?.menu?.add(
                                0,
                                index,
                                index,
                                if (index == 0) R.string.file_picker_tv_internal_storage else R.string.file_picker_tv_sd_card
                            )
                        }

                        // Set handle event click
                        popup?.setOnMenuItemClickListener { item ->
                            /*
                             * In-case data is invalid
                             */
                            if (item.itemId < 0 || item.itemId >= strExternalStoragePaths.size) {
                                return@setOnMenuItemClickListener false
                            }
                            // Get new path of selected menu item
                            val strNewPath = strExternalStoragePaths[item.itemId]
                            // In-case the path is null
                            if (TextUtils.isEmpty(strNewPath)) {
                                return@setOnMenuItemClickListener false
                            }
                            // Handle UI/ update view after get new root path
                            pickerConfig.setCustomRootPath(strNewPath)

                            // Clear data and re-load list
                            navDataSource.clear()
                            // Reload list.
                            loadList()
                            true
                        }
                        popup?.show()
                    } else {
                        navAdapter?.let {
                            saveCurrPos(it.data.last(), position)
                        }
                        // 如果是文件夹,则进入
                        enterDirAndUpdateUI(item)
                    }
                }
            }
        }
    }

All rights, you can implement it for your own caused my code is changed so much so I can't publish the sample.

The video below is demoed my code.

device-2019-11-28-001403.webm.zip

Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'

java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'
at kotlinx.coroutines.internal.q.a(MainDispatchers.kt:72)
at kotlinx.coroutines.internal.q.a(MainDispatchers.kt:53)
at kotlinx.coroutines.ac.a(Dispatched.kt:410)
at kotlinx.coroutines.a.a.a(Cancellable.kt:25)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.a.a(AbstractCoroutine.kt:154)
at kotlinx.coroutines.d.a(Builders.common.kt:54)
at kotlinx.coroutines.c.a(Unknown Source)
at kotlinx.coroutines.d.a(Builders.common.kt:47)
at kotlinx.coroutines.c.a(Unknown Source)
at me.rosuh.filepicker.FilePickerActivity.g(FilePickerActivity.kt:121)
at me.rosuh.filepicker.FilePickerActivity.onCreate(FilePickerActivity.kt:73)
at android.app.Activity.performCreate(Activity.java:6497)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2519)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2626)
at android.app.ActivityThread.access$1100(ActivityThread.java:170)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1494)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:179)
at android.app.ActivityThread.main(ActivityThread.java:5769)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674)
09-04 17:26:25.101 1606-2697/? E/libprocessgroup: failed to kill 1 processes for processgroup 2676
09-04 17:26:25.230 2832-2832/? E/Tinker.ReflectApp: replaceApplicationLike delegateClass:class com.tencent.bugly.beta.tinker.TinkerApplicationLike
09-04 17:26:25.364 2832-2832/? E/ExtMediaPlayer-JNI: env->IsInstanceOf fails
09-04 17:26:25.364 2832-2832/? E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0
09-04 17:26:25.364 2832-2832/? E/ExtMediaPlayer-JNI: env->IsInstanceOf fails
09-04 17:26:25.364 2832-2832/? E/MediaPlayer-JNI: JNIMediaPlayerFactory: bIsQCMediaPlayerPresent 0

选择存储位置问题

我看了下FileUtils.kt 中
方法fun getRootFile():File
发现无论我设置说明存储类型他都说返回同一个位置的路径:
File(Environment.getExternalStorageDirectory().absoluteFile.toURI())

筛选文件类型无效

比如只想显示视频,

AbstractFileFilter aFilter = new AbstractFileFilter() {
                   @NotNull
                   @Override
                   public ArrayList<FileItemBeanImpl> doFilter(@NotNull final ArrayList<FileItemBeanImpl> arrayList) {
                       ArrayList<FileItemBeanImpl> fileItemBeans = new ArrayList<>();
                       for (FileItemBeanImpl fileItemBean : arrayList){

                           if (fileItemBean.isChecked() || fileItemBean.getFileType() instanceof VideoFileType){
                               fileItemBeans.add(fileItemBean);
                           }
                       }
                       return fileItemBeans;
                   }
               };
 FilePickerManager.INSTANCE
                       .from(mActivity)
                       .filter(aFilter)
                       .maxSelectable(1)
                       .setTheme(R.style.FilePickerThemeRail)
                       .showCheckBox(false)
                       .forResult(FilePickerManager.REQUEST_CODE);

还是可以看到并选择其他类型的文件

文字显示有异常

系统为中文的情况下,默认显示为英文,此时自定义文字内容,将文字改为中文,初次打开依旧为英文(初次打开自定义文字没生效,需要选中或打开打开下一级的文件夹文字才会更新)

能否指定默认打开目录,而非指定根目录

目前可用CustomRootPath指定打开时的根目录,但因为是根目录的关系,选择时无法返回上一级目录。
能否指定一个起始目录,我打开选择器后会进入该起始目录,如果起始目录非根目录时,还可以正常返回上一级目录。
这在实现有记忆上一次用户选择的路径的功能的文件选择器时很有用。

recyclerview滑动优化建议

快速滑动时,列表会掉帧严重。
不建议在recycleView的item中使用android.support.constraint.ConstraintLayout约束布局作为根布局,其重复测量会导致严重性能问题。
建议使用传统的布局控件,例如线性布局、帧布局等等

文件扫描性能优化建议

1、使用了kotlin,为何还要使用findViewById。是有什么特殊的原因么?

2、进入activity后,扫描文件的工作应该放到子线程协程中去做,就不出现莫名其妙的启动等待时间。(个人建议放入协程,第一轻量级,第二异地可以当同步,不影响现有逻辑顺序)

自定义过滤器(可选)

// 自定义过滤器(可选)
.filter(fileFilter)

未添加过滤器会导致崩溃,因此过滤器为必须添加,并不是可选

demo中筛选类型无效

在oppo a59s 和红米note6手机上运行demo,点击只展示文件夹和只展示图片,分别无效。

返回键的优化性建议

现在每次打开下一级目录,再返回上一级目录时,没有记录上次滑动的位置,导致体验上很差。

单选问题

.showCheckBox(false) 没有作用 , 能不能点击文件列表item, 直接回调, 而不是勾了复选框 , 再点击确认 , 在返回, 即FileItemOnClickListenerImpl 这个里面提供下单项点击的实现

AudioFileType ogg

please add ogg to audio filter

AudioFileType :
return when (suffix){
"aif", "iff", "m3u", "m4a", "mid", "mp3", "mpa", "wav", "wma","ogg" -> {
true
}
else -> {
false
}
}

因框架的application文件"APP"导致的问题

背景:java代码中调用
在0.3.0 中 没有"APP" 这个application文件
故没有问题

在0.4.0中 因为框架中有"APP"这个application文件
导致与项目本身的application文件冲突,需要加入 'tools:replace="android:name"' 来解决
当加入 'tools:replace="android:name"' 解决后 调用文件选择器时,会出现如下崩溃
kotlin.UninitializedPropertyAccessException: lateinit property appResources has not been initialized
at me.rosuh.filepicker.App.access$getAppResources$cp(App.kt:11)
at me.rosuh.filepicker.App$Companion.getAppResources(App.kt:14)
at me.rosuh.filepicker.config.FilePickerConfig.(FilePickerConfig.kt:34)
at me.rosuh.filepicker.config.FilePickerManager.from(FilePickerManager.kt:30)
at com.xxx.xxx.xxx.xxxActivity.selectDocument(xxxActivity.java:608)
at com.xxx.xxx.xxx.xxxActivity$5.onClick(xxxActivity.java:291)
at android.view.View.performClick(View.java:6897)
at android.view.View$PerformClick.run(View.java:26101)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

框架中的application文件"APP" 中 appResources 未初始化?
这难道是java代码与Kotlin代码联结的问题么?

指定选择数量后 与顶部toolbar中的全选功能冲突

1.指定数量功能失效:
当指定选择数量后,点击全选功能,仍然可以选中全部(超出指定数量),并且过滤掉的文件貌似也被选中,但是不会将数据返回至activity中.
2.能选中过滤掉的文件
当指定选择数量后,在文件过滤后,点击全选,估计是选中了过滤掉的文件,只是没显示,然后点击确认 会提示选择超过多个,界面上看起来并没有选这么多,可能选中的那些是过滤掉的文件,

Localization

How to translate "Select all" and "Done" buttons ?

Remote control

Unable to open folder and mark file using remote control.

单选

是否可以实现单选?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.