diff --git a/README.md b/README.md index d3b3b32..fd1cd44 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ [![](https://jitpack.io/v/me.rosuh/AndroidFilePicker.svg)](https://jitpack.io/#me.rosuh/AndroidFilePicker) -[中文简体](https://github.com/rosuH/AndroidFilePicker/blob/master/README_CN.md) +[中文简体](./README_CN.md) + +If you're using `0.x` version, checkout the [README_0.x](./README_0.x.md) file. Well, it doesn't have a name like Rocky, Cosmos or Fish. Android File Picker, like its name, is a local file selector framework. Some of his characteristics are described below: @@ -22,30 +24,35 @@ Well, it doesn't have a name like Rocky, Cosmos or Fish. Android File Picker, li - Apply different themes, including four built-in themes and custom themes - More to find out yourself -| Rail Style(default) | Reply Style | Crane Style | Shrine Style | +| Rail | Reply | Crane | Shrine | | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/default_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/reply_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/crane_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/shrine_theme.png) | ## Version Compatibility It depends on your targetAPI. -- `targetAPI <= 28`, no problem at all ;) -- `targetAPI == 29`, please enable `requestLegacyExternalStorage` feature for your project : D -- `targetAPI == 29` - - When running on Android 11 and above, only media files (images, audio and video) can be read, but nothing else can be accessed (e.g. PDF documents, apk binary files, etc.) +- `targetAPI > 33`, may be you are finding [photo picker](https://developer.android.com/about/versions/14/changes/partial-photo-video-access?hl=zh-cn#media-reselection) +- `targetAPI == 33` + - Handle [media permissions](https://developer.android.com/training/data-storage/shared/media#access-other-apps-files) by your onw + - This lib will only show media files which your app has permission to access +- `targetAPI <= 33` + - Set `android:requestLegacyExternalStorage="true"` in your `AndroidManifest.xml` file + - Handler `android.permission.READ_EXTERNAL_STORAGE` permission by your own + - This lib will show all files in your storage -Please check out this issue: [All About Scope Storage. ](https://github.com/rosuH/AndroidFilePicker/issues/146) ## Download -Gradle: +[Gradle](https://docs.jitpack.io/android/#installing): In your project `build.gradle`: ```xml -allprojects { +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { - ... - maven { url 'https://jitpack.io' } + google() + mavenCentral() + maven { url 'https://jitpack.io' } } } ``` @@ -65,11 +72,8 @@ Check out [releases page](https://github.com/rosuH/AndroidFilePicker/releases) t ### Permission -The library requires one permissions: - -- `android.permission.READ_EXTERNAL_STORAGE` - -If you do not have permission to apply, this framework will check and apply at startup. +You should request permission by yourself, this lib will not request permission for you. +See [Version Compatibility](#version-compatibility) for more details. ### Launch 🚀 diff --git a/README_0.x.md b/README_0.x.md new file mode 100644 index 0000000..e34b728 --- /dev/null +++ b/README_0.x.md @@ -0,0 +1,124 @@ +![Banner](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/AndroidFilePicker_Banner_Dr_Sugiyama.png) + +# Android File Picker🛩️ + +[![](https://jitpack.io/v/me.rosuh/AndroidFilePicker.svg)](https://jitpack.io/#me.rosuh/AndroidFilePicker) + +[中文简体](./README_CN_1.x.md) + +Well, it doesn't have a name like Rocky, Cosmos or Fish. Android File Picker, like its name, is a local file selector framework. Some of his characteristics are described below: + +- Launcher in Activity or Fragment + - Start with a single line of code +- Browse and select all files in local storage + - Custom Root path to start + - Built-in default file type and file discriminator + - Or you can implement the file type yourself +- Built in Single Choice mode and Multiple Choice mode. +- Custom list filter + - Just want to show pictures(Or videos, audio...)? No problem! + - Of course, you can just display the folder +- Custom item click event: only need to implement the listener +- Apply different themes, including four built-in themes and custom themes +- More to find out yourself + +| Rail Style(default) | Reply Style | Crane Style | Shrine Style | +| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | +| ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/default_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/reply_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/crane_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/shrine_theme.png) | + +## Version Compatibility +It depends on your targetAPI. + +- `targetAPI <= 28`, no problem at all ;) +- `targetAPI == 29`, please enable `requestLegacyExternalStorage` feature for your project : D +- `targetAPI == 29` + - When running on Android 11 and above, only media files (images, audio and video) can be read, but nothing else can be accessed (e.g. PDF documents, apk binary files, etc.) + +Please check out this issue: [All About Scope Storage. ](https://github.com/rosuH/AndroidFilePicker/issues/146) +## Download + +Gradle: + +In your project `build.gradle`: + +```xml +allprojects { + repositories { + ... + maven { url 'https://jitpack.io' } + } +} +``` + +In your module `build.gradle`: + +```xml +dependencies { + implementation 'me.rosuh:AndroidFilePicker:$latest_version' +} +``` +This lib now support AndroidX, check the version below. + +Check out [releases page](https://github.com/rosuH/AndroidFilePicker/releases) to see more versions. + +## Usage 📑 + +### Permission + +The library requires one permissions: + +- `android.permission.READ_EXTERNAL_STORAGE` + +If you do not have permission to apply, this framework will check and apply at startup. + +### Launch 🚀 + +```kotlin +FilePickerManager + .from(context) + .forResult(FilePickerManager.REQUEST_CODE) +``` + + + +### Receive Result + +In `onActivityResult()` callback of the starting `Activity` or `Fragment`: + +```kotlin +override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + when (requestCode) { + FilePickerManager.instance.REQUEST_CODE -> { + if (resultCode == Activity.RESULT_OK) { + val list = FilePickerManager.instance.obtainData() + // do your work + } else { + Toast.makeText(this@SampleActivity, "You didn't choose anything~", Toast.LENGTH_SHORT).show() + } + } + } +} +``` + +The result is a path list of the selected file (`ArrayList()`). + + +## Docs + +- [Source Code Explanation](https://github.com/rosuH/AndroidFilePicker/wiki/4.-%E7%A4%BA%E4%BE%8B%E5%8F%8A%E8%A7%A3%E9%87%8A). + +- [Change Log](https://github.com/rosuH/AndroidFilePicker/wiki/Change-Log) + +- [TODO](https://github.com/rosuH/AndroidFilePicker/wiki/TODO) + + + +--- + +## Special Thanks To: + +- [whichName](https://github.com/whichname) +- [Matisse](https://github.com/zhihu/Matisse) +- [Default Icon Author Shulk](http://iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.d9df05512&cid=11271) +- [Theme Color](https://material.io/design/material-studies/about-our-material-studies.html) +- [Empty icon](https://github.com/rosuH/AndroidFilePicker/blob/master/filepicker/src/main/res/drawable/ic_empty_file_list_file_picker.xml) made by [freepik](https://www.freepik.com/) from www.flaticon.com diff --git a/README_CN.md b/README_CN.md index dd19785..444ded9 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,153 +1,62 @@ ![Banner](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/AndroidFilePicker_Banner_Dr_Sugiyama.png) -# AndroidFilePicker +# Android File Picker🛩️ [![](https://jitpack.io/v/me.rosuh/AndroidFilePicker.svg)](https://jitpack.io/#me.rosuh/AndroidFilePicker) +如果你使用的是 `1.x` 版本,请查看 [README_0.x](./README_CN_0.x.md) 文件。 +它没有像 Rocky、Cosmos 或 Fish 这样的名字。Android File Picker,正如其名,是一个本地文件选择框架。以下是它的一些特点: -它没有像 Rocky,Cosmos 或是 Peppa 这样的名字。 Android File Picker 正如其名,是一个本地文件选择器框架。 他的一些特征如下所述: - -- 在 `Activity` 或 `Fragment` 中启动 - - 从一行代码开始 -- 浏览本地存储中的所有文件 - - 内置默认文件类型和文件鉴别器 - - 或者您可以自己实现文件类型 -- 内置了单选模式和多选模式 +- 在 Activity 或 Fragment 中启动 + - 一行代码启动 +- 浏览和选择本地存储中的所有文件 + - 自定义根路径开始 + - 内置默认文件类型和文件区分器 + - 或者你可以自己实现文件类型 +- 内置单选模式和多选模式。 - 自定义列表过滤器 - - 只想显示图片(或视频,音频......)? 没问题! - - 当然,您也可只显示文件夹 -- 自定义`item`点击事件:只需要实现监听器 -- 四个内置主题和自定义主题 -- 还有更多待您自己探索的特性(?) - - + - 只想显示图片(或视频、音频...)?没问题! + - 当然,你也可以只显示文件夹 +- 自定义条目点击事件:只需实现监听器 +- 应用不同的主题,包括四个内置主题和自定义主题 +- 更多功能等你发现 -| Rail Style(default) | Reply Style | Crane Style | Shrine Style | +| Rail | Reply | Crane | Shrine | | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/default_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/reply_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/crane_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/shrine_theme.png) | ## 版本兼容性 -这取决于您的 targetAPI : +取决于你的 targetAPI。 -- `targetAPI <= 28`,完全没有问题 ;) -- `targetAPI == 29`,请为您的项目启用 `requestLegacyExternalStorage` 特性:D -- `targetAPI == 29` - - 当运行于 Android 11以及以上的平台时,仅可以读取媒体文件(图片、音视频),除此均无法访问(比如PDF文档、apk 二进制文件等) +- `targetAPI > 33`,也许你正在寻找 [照片选择器](https://developer.android.com/about/versions/14/changes/partial-photo-video-access?hl=zh-cn#media-reselection) +- `targetAPI == 33` + - 处理[媒体权限](https://developer.android.com/training/data-storage/shared/media#access-other-apps-files)由你自己处理 + - 此库将仅显示你的应用有权限访问的媒体文件 +- `targetAPI <= 33` + - 在你的 `AndroidManifest.xml` 文件中设置 `android:requestLegacyExternalStorage="true"` + - 由你自己处理 `android.permission.READ_EXTERNAL_STORAGE` 权限 + - 此库将显示存储中的所有文件 -请参看 issue: [All About Scope Storage. ](https://github.com/rosuH/AndroidFilePicker/issues/146) -## 下载使用 +## 下载 -1. 在你的项目中添加依赖 +[Gradle](https://docs.jitpack.io/android/#installing): -现在项目 `build.gradle` 配置文件添加仓库: +在项目的 `build.gradle` 文件中: -```xml -allprojects { +```gradle +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { - ... - maven { url 'https://jitpack.io' } + google() + mavenCentral() + maven { url 'https://jitpack.io' } } } -``` +在模块的 build.gradle 文件中: -然后在子模块(`app`)的配置文件添加依赖: - -```xml +```gradle dependencies { - implementation 'me.rosuh:AndroidFilePicker:latest_version' -} -``` - -`latest_version` 请自行替换成 [最新版本](https://github.com/rosuH/AndroidFilePicker/releases) - - - -## 使用 - -### 权限 - -此库需要一个权限: - -- `android.permission.READ_EXTERNAL_STORAGE` - -如果您没有提前授予,这个库会自动申请该权限的。 - -### 开始使用 - -简单的链式调用示意: - -```kotlin -FilePickerManager - .from(context) - .forResult(FilePickerManager.REQUEST_CODE) -``` - -现在你已经起飞了🛩️...(真的只有两行) - - -### 获取结果 - -*获取结果*:`onActivityResult`接受消息,然后调用`FilePickerManager.obtainData()`获取保存的数据,**结果是所选取文件的路径列表(`ArrayList()`)** - -```kotlin -override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when (requestCode) { - FilePickerManager.instance.REQUEST_CODE -> { - if (resultCode == Activity.RESULT_OK) { - val list = FilePickerManager.instance.obtainData() - // do your work - } else { - Toast.makeText(this@SampleActivity, "没有选择任何东西~", Toast.LENGTH_SHORT).show() - } - } - } + implementation 'me.rosuh:AndroidFilePicker:$latest_version' } -``` - -### 更多示例 - -来翻翻我写的[飞行手册](https://github.com/rosuH/AndroidFilePicker/wiki)吧? - -或者想看看[主题配色](https://github.com/rosuH/AndroidFilePicker/wiki/3.-%E9%85%8D%E7%BD%AE%E9%80%89%E9%A1%B9#2-%E4%B8%BB%E9%A2%98%E5%B1%95%E7%A4%BA)? - -## 功能 & 特点 - -1. 链式调用 -2. 默认选中实现 - - 点击条目(`item`)无默认实现 - - 点击`CheckBox`为选中 - - 长按条目为更改选中状态:选中/取消选中 -3. 内置四种主题配色 + 可自定义配色 - - 查看主题颜色示意图,然后调用`setTheme()`传入自定义主题 -4. 默认实现多种文件类型 - - 实现`IFileType`接口来实现你的文件类型 - - 实现`AbstractFileType`抽象类来实现你的文件类型甄别器 -5. 公开文件过滤接口 - - 实现`AbstractFileFilter`抽象类来定制你自己的文件过滤器,这样可以控制文件列表的展示内容 -6. 多种可配置选项 - 1. 选中时是否忽略文件夹 - 2. 是否显示隐藏文件夹(以符号`.`开头的,视为隐藏文件或隐藏文件夹) - 3. 可配置导航栏的文本,默认显示、多选文本、取消选择文本以及根目录默认名称 -7. 公开条目(`item`)选择监听器,可自定义条目被点击的实现 - -## 其他 - -- [部分源码说明](https://github.com/rosuH/AndroidFilePicker/wiki/%E9%83%A8%E5%88%86%E6%BA%90%E7%A0%81%E8%AF%B4%E6%98%8E)。 - -- [更新日志](https://github.com/rosuH/AndroidFilePicker/wiki/Change-Log) - -- [TODO](https://github.com/rosuH/AndroidFilePicker/wiki/TODO) - - - ---- - -## Special Thanks To: - -- [*1 @whichName](https://github.com/whichname) -- [BRVAH](https://github.com/CymChad/BaseRecyclerViewAdapterHelper) -- [Matisse](https://github.com/zhihu/Matisse) -- [默认图标作者 Shulk](http://iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.d9df05512&cid=11271) -- [主题配色](https://material.io/design/material-studies/about-our-material-studies.html) -- [Empty icon](https://github.com/rosuH/AndroidFilePicker/blob/master/filepicker/src/main/res/drawable/ic_empty_file_list_file_picker.xml) made by [freepik](https://www.freepik.com/) from www.flaticon.com +``` \ No newline at end of file diff --git a/README_CN_0.x.md b/README_CN_0.x.md new file mode 100644 index 0000000..ee3c5f5 --- /dev/null +++ b/README_CN_0.x.md @@ -0,0 +1,149 @@ +![Banner](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/AndroidFilePicker_Banner_Dr_Sugiyama.png) + +# AndroidFilePicker + +[![](https://jitpack.io/v/me.rosuh/AndroidFilePicker.svg)](https://jitpack.io/#me.rosuh/AndroidFilePicker) + + + +它没有像 Rocky,Cosmos 或是 Peppa 这样的名字。 Android File Picker 正如其名,是一个本地文件选择器框架。 他的一些特征如下所述: + +- 在 `Activity` 或 `Fragment` 中启动 + - 从一行代码开始 +- 浏览本地存储中的所有文件 + - 内置默认文件类型和文件鉴别器 + - 或者您可以自己实现文件类型 +- 内置了单选模式和多选模式 +- 自定义列表过滤器 + - 只想显示图片(或视频,音频......)? 没问题! + - 当然,您也可只显示文件夹 +- 自定义`item`点击事件:只需要实现监听器 +- 四个内置主题和自定义主题 +- 还有更多待您自己探索的特性(?) + + + +| Rail Style(default) | Reply Style | Crane Style | Shrine Style | +| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | +| ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/default_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/reply_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/crane_theme.png) | ![](https://raw.githubusercontent.com/rosuH/AndroidFilePicker/master/images/shrine_theme.png) | + +## 版本兼容性 +这取决于您的 targetAPI : + +- `targetAPI <= 28`,完全没有问题 ;) +- `targetAPI == 29`,请为您的项目启用 `requestLegacyExternalStorage` 特性:D +- `targetAPI == 29` + - 当运行于 Android 11以及以上的平台时,仅可以读取媒体文件(图片、音视频),除此均无法访问(比如PDF文档、apk 二进制文件等) + +请参看 issue: [All About Scope Storage. ](https://github.com/rosuH/AndroidFilePicker/issues/146) +## 下载使用 + +1. 在你的项目中添加依赖 + +现在项目 `build.gradle` 配置文件添加仓库: + +```xml +allprojects { + repositories { + ... + maven { url 'https://jitpack.io' } + } +} +``` + +然后在子模块(`app`)的配置文件添加依赖: + +```xml +dependencies { + implementation 'me.rosuh:AndroidFilePicker:latest_version' +} +``` + +`latest_version` 请自行替换成 [最新版本](https://github.com/rosuH/AndroidFilePicker/releases) + + + +## 使用 + +### 权限 + +你需要自己申请权限,这个库不会为你申请权限。看看[版本兼容性](#版本兼容性)了解更多细节。 + +### 开始使用 + +简单的链式调用示意: + +```kotlin +FilePickerManager + .from(context) + .forResult(FilePickerManager.REQUEST_CODE) +``` + +现在你已经起飞了🛩️...(真的只有两行) + + +### 获取结果 + +*获取结果*:`onActivityResult`接受消息,然后调用`FilePickerManager.obtainData()`获取保存的数据,**结果是所选取文件的路径列表(`ArrayList()`)** + +```kotlin +override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + when (requestCode) { + FilePickerManager.instance.REQUEST_CODE -> { + if (resultCode == Activity.RESULT_OK) { + val list = FilePickerManager.instance.obtainData() + // do your work + } else { + Toast.makeText(this@SampleActivity, "没有选择任何东西~", Toast.LENGTH_SHORT).show() + } + } + } +} +``` + +### 更多示例 + +来翻翻我写的[飞行手册](https://github.com/rosuH/AndroidFilePicker/wiki)吧? + +或者想看看[主题配色](https://github.com/rosuH/AndroidFilePicker/wiki/3.-%E9%85%8D%E7%BD%AE%E9%80%89%E9%A1%B9#2-%E4%B8%BB%E9%A2%98%E5%B1%95%E7%A4%BA)? + +## 功能 & 特点 + +1. 链式调用 +2. 默认选中实现 + - 点击条目(`item`)无默认实现 + - 点击`CheckBox`为选中 + - 长按条目为更改选中状态:选中/取消选中 +3. 内置四种主题配色 + 可自定义配色 + - 查看主题颜色示意图,然后调用`setTheme()`传入自定义主题 +4. 默认实现多种文件类型 + - 实现`IFileType`接口来实现你的文件类型 + - 实现`AbstractFileType`抽象类来实现你的文件类型甄别器 +5. 公开文件过滤接口 + - 实现`AbstractFileFilter`抽象类来定制你自己的文件过滤器,这样可以控制文件列表的展示内容 +6. 多种可配置选项 + 1. 选中时是否忽略文件夹 + 2. 是否显示隐藏文件夹(以符号`.`开头的,视为隐藏文件或隐藏文件夹) + 3. 可配置导航栏的文本,默认显示、多选文本、取消选择文本以及根目录默认名称 +7. 公开条目(`item`)选择监听器,可自定义条目被点击的实现 + +## 其他 + +- [部分源码说明](https://github.com/rosuH/AndroidFilePicker/wiki/%E9%83%A8%E5%88%86%E6%BA%90%E7%A0%81%E8%AF%B4%E6%98%8E)。 + +- [更新日志](https://github.com/rosuH/AndroidFilePicker/wiki/Change-Log) + +- [TODO](https://github.com/rosuH/AndroidFilePicker/wiki/TODO) + + + +--- + +## Special Thanks To: + +- [*1 @whichName](https://github.com/whichname) +- [BRVAH](https://github.com/CymChad/BaseRecyclerViewAdapterHelper) +- [Matisse](https://github.com/zhihu/Matisse) +- [默认图标作者 Shulk](http://iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.d9df05512&cid=11271) +- [主题配色](https://material.io/design/material-studies/about-our-material-studies.html) +- [Empty icon](https://github.com/rosuH/AndroidFilePicker/blob/master/filepicker/src/main/res/drawable/ic_empty_file_list_file_picker.xml) made by [freepik](https://www.freepik.com/) from www.flaticon.com diff --git a/build.gradle b/build.gradle index 79cf963..cde0933 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version="1.6.0" + ext.kotlin_version="1.9.20" repositories { google() mavenCentral() @@ -10,7 +10,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:7.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath "org.jlleitschuh.gradle:ktlint-gradle:10.0.0" diff --git a/filepicker/build.gradle b/filepicker/build.gradle index 00d40ca..3c53526 100644 --- a/filepicker/build.gradle +++ b/filepicker/build.gradle @@ -2,18 +2,15 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'maven-publish' group='com.github.rosuH' android { - compileSdkVersion 30 - defaultConfig { minSdkVersion 16 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" + compileSdk 33 + targetSdkVersion 33 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -36,6 +33,20 @@ android { } } + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + from components.release + groupId = 'me.rosuh' + artifactId = 'AndroidFilePicker' + version = '1.0' + } + } + } +} + dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) compileOnly 'com.squareup.picasso:picasso:2.5.2' @@ -50,6 +61,6 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.1.2' implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } diff --git a/filepicker/src/androidTest/java/me/rosuh/filepicker/ExampleInstrumentedTest.java b/filepicker/src/androidTest/java/me/rosuh/filepicker/ExampleInstrumentedTest.java index 7fe39a7..fdfb9b9 100644 --- a/filepicker/src/androidTest/java/me/rosuh/filepicker/ExampleInstrumentedTest.java +++ b/filepicker/src/androidTest/java/me/rosuh/filepicker/ExampleInstrumentedTest.java @@ -20,8 +20,5 @@ public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("me.rosuh.filepicker.test", appContext.getPackageName()); } } diff --git a/filepicker/src/main/AndroidManifest.xml b/filepicker/src/main/AndroidManifest.xml index 0f5f53b..499f970 100644 --- a/filepicker/src/main/AndroidManifest.xml +++ b/filepicker/src/main/AndroidManifest.xml @@ -1,11 +1,11 @@ - + package="me.rosuh.filepicker"> + android:name=".FilePickerActivity" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" + android:exported="false" + android:configChanges="orientation|screenSize" /> diff --git a/filepicker/src/main/java/me/rosuh/filepicker/FilePickerActivity.kt b/filepicker/src/main/java/me/rosuh/filepicker/FilePickerActivity.kt index d6ecac0..9137bf9 100644 --- a/filepicker/src/main/java/me/rosuh/filepicker/FilePickerActivity.kt +++ b/filepicker/src/main/java/me/rosuh/filepicker/FilePickerActivity.kt @@ -1,6 +1,10 @@ package me.rosuh.filepicker import android.Manifest +import android.Manifest.permission.READ_EXTERNAL_STORAGE +import android.Manifest.permission.READ_MEDIA_AUDIO +import android.Manifest.permission.READ_MEDIA_IMAGES +import android.Manifest.permission.READ_MEDIA_VIDEO import android.annotation.SuppressLint import android.app.Activity import android.content.Intent @@ -14,7 +18,6 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.animation.AnimationUtils @@ -70,7 +73,7 @@ class FilePickerActivity : AppCompatActivity(), View.OnClickListener, val root = FileUtils.getRootFile() var curPath = customRootPathFile.absolutePath while (curPath != root.parent && !curPath.isNullOrBlank()) { - Log.i("loadFileRunnable", "curPath = $curPath") + pickerConfig.logger.i("loadFileRunnable", "curPath = $curPath") val f = File(curPath) val fileNavBeanImpl = FileNavBeanImpl( FileUtils.getDirAlias(f), @@ -82,13 +85,16 @@ class FilePickerActivity : AppCompatActivity(), View.OnClickListener, pickerConfig.resetCustomFile() customRootPathFile } + navDataSource.isEmpty() && pickerConfig.isSkipDir -> { FileUtils.getRootFile() } + navDataSource.isEmpty() && !pickerConfig.isSkipDir -> { // 如果是文件夹作为可选项时,需要让根目录也作为 item 被点击,故而取根目录上级作为 rootFiles FileUtils.getRootFile().parentFile } + else -> { File(navDataSource.last().dirPath) } @@ -183,38 +189,44 @@ class FilePickerActivity : AppCompatActivity(), View.OnClickListener, override fun onDestroy() { super.onDestroy() - Log.i(TAG, "onDestroy") + pickerConfig.logger.i(TAG, "onDestroy") try { loadingFuture?.cancel(true) loadingFuture = null } catch (e: Exception) { - Log.e(TAG, "onDestroy: ", e) + pickerConfig.logger.e(TAG, "onDestroy: ", e) } val shouldShutDownThreadPool = pickerConfig.threadPool != loadingThreadPool || pickerConfig.threadPoolAutoShutDown if (!loadingThreadPool.isShutdown && shouldShutDownThreadPool) { - Log.i(TAG, "shutdown thread pool") + pickerConfig.logger.i(TAG, "shutdown thread pool") loadingThreadPool.shutdown() } currOffsetMap.clear() currPosMap.clear() } - private fun isPermissionGrated() = ContextCompat.checkSelfPermission( - this, - Manifest.permission.READ_EXTERNAL_STORAGE - ) == PackageManager.PERMISSION_GRANTED + private fun isPermissionGrated(): Boolean { + val permissionArray = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO) + } else { + arrayOf(READ_EXTERNAL_STORAGE) + } + return permissionArray.any { + ContextCompat.checkSelfPermission( + this@FilePickerActivity, + it + ) == PackageManager.PERMISSION_GRANTED + } + } /** * 申请权限 */ private fun requestPermission() { - ActivityCompat.requestPermissions( - this@FilePickerActivity, - arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), - FILE_PICKER_PERMISSION_REQUEST_CODE - ) + pickerConfig.logger.e(TAG, "You have no permission to read media files.") + setLoadingFinish() } override fun onRequestPermissionsResult( @@ -292,12 +304,15 @@ class FilePickerActivity : AppCompatActivity(), View.OnClickListener, R.style.FilePickerThemeCrane -> { R.array.crane_swl_colors } + R.style.FilePickerThemeReply -> { R.array.reply_swl_colors } + R.style.FilePickerThemeShrine -> { R.array.shrine_swl_colors } + else -> { R.array.rail_swl_colors } @@ -337,17 +352,20 @@ class FilePickerActivity : AppCompatActivity(), View.OnClickListener, return } if (Environment.getExternalStorageState() != MEDIA_MOUNTED) { - Log.e( + pickerConfig.logger.e( TAG, "External storage is not available ====>>> " + "Environment.getExternalStorageState() != MEDIA_MOUNTED" ) return } try { - Log.i(TAG, "loadList in ${Thread.currentThread()} in $loadingThreadPool") + pickerConfig.logger.i( + TAG, + "loadList in ${Thread.currentThread()} in $loadingThreadPool" + ) loadingFuture = loadingThreadPool.submit(loadFileRunnable) } catch (e: RejectedExecutionException) { - Log.e(TAG, "submit job failed") + pickerConfig.logger.e(TAG, "submit job failed") } } @@ -447,6 +465,7 @@ class FilePickerActivity : AppCompatActivity(), View.OnClickListener, ) } } + R.id.item_nav_file_picker -> { if (file.isDirectory) { (rvNav?.adapter as? FileNavAdapter)?.let { @@ -473,6 +492,7 @@ class FilePickerActivity : AppCompatActivity(), View.OnClickListener, item ?: return enterDirAndUpdateUI(item) } + else -> { val item = (adapter as FileListAdapter).getItem(position) ?: return // Check the lib users whether if intercept the click event. @@ -661,6 +681,7 @@ class FilePickerActivity : AppCompatActivity(), View.OnClickListener, this@FilePickerActivity.setResult(Activity.RESULT_OK, intent) finish() } + R.id.btn_go_back_file_picker -> { onBackPressed() } diff --git a/filepicker/src/main/java/me/rosuh/filepicker/adapter/RecyclerViewListener.kt b/filepicker/src/main/java/me/rosuh/filepicker/adapter/RecyclerViewListener.kt index 99d03da..d192517 100644 --- a/filepicker/src/main/java/me/rosuh/filepicker/adapter/RecyclerViewListener.kt +++ b/filepicker/src/main/java/me/rosuh/filepicker/adapter/RecyclerViewListener.kt @@ -73,7 +73,7 @@ class RecyclerViewListener( private val checkBoxLeft = screenWidth * (1 - 0.1370) inner class ItemTouchHelperGestureListener : GestureDetector.SimpleOnGestureListener() { - override fun onSingleTapUp(e: MotionEvent?): Boolean { + override fun onSingleTapUp(e: MotionEvent): Boolean { val childView = recyclerView.findChildViewUnder(e!!.x, e.y) childView ?: return false when (childView.id) { @@ -104,7 +104,7 @@ class RecyclerViewListener( return true } - override fun onLongPress(e: MotionEvent?) { + override fun onLongPress(e: MotionEvent) { val childView = recyclerView.findChildViewUnder(e!!.x, e.y) childView ?: return when (childView.id) { diff --git a/filepicker/src/main/java/me/rosuh/filepicker/config/FilePickerConfig.kt b/filepicker/src/main/java/me/rosuh/filepicker/config/FilePickerConfig.kt index 9011681..198fbcb 100644 --- a/filepicker/src/main/java/me/rosuh/filepicker/config/FilePickerConfig.kt +++ b/filepicker/src/main/java/me/rosuh/filepicker/config/FilePickerConfig.kt @@ -1,12 +1,14 @@ package me.rosuh.filepicker.config import android.content.Intent +import android.util.Log import androidx.annotation.NonNull import androidx.annotation.StringRes import me.rosuh.filepicker.FilePickerActivity import me.rosuh.filepicker.R import me.rosuh.filepicker.engine.ImageEngine import me.rosuh.filepicker.filetype.FileType +import me.rosuh.filepicker.utils.DefaultLogger import java.io.File import java.util.concurrent.* @@ -163,6 +165,9 @@ class FilePickerConfig(private val pickerManager: FilePickerManager) { var customImageEngine: ImageEngine? = null private set + var logger: ILog = DefaultLogger + private set + fun showHiddenFiles(isShow: Boolean): FilePickerConfig { isShowHiddenFiles = isShow return this @@ -348,6 +353,15 @@ class FilePickerConfig(private val pickerManager: FilePickerManager) { return this } + /** + * 允许使用你项目中的 Logger + * Allow the use of logger in your project + */ + fun log(logger: ILog): FilePickerConfig { + this.logger = logger + return this + } + fun forResult(requestCode: Int) { val activity = pickerManager.contextRef?.get() val fragment = pickerManager.fragmentRef?.get() diff --git a/filepicker/src/main/java/me/rosuh/filepicker/config/ILog.kt b/filepicker/src/main/java/me/rosuh/filepicker/config/ILog.kt new file mode 100644 index 0000000..acab287 --- /dev/null +++ b/filepicker/src/main/java/me/rosuh/filepicker/config/ILog.kt @@ -0,0 +1,10 @@ +package me.rosuh.filepicker.config + +interface ILog { + fun d(tag: String, msg: String) + fun e(tag: String, msg: String, tr: Throwable? = null) + fun i(tag: String, msg: String) + fun v(tag: String, msg: String) + fun w(tag: String, msg: String) + fun wtf(tag: String, msg: String) +} \ No newline at end of file diff --git a/filepicker/src/main/java/me/rosuh/filepicker/utils/DefaultLogger.kt b/filepicker/src/main/java/me/rosuh/filepicker/utils/DefaultLogger.kt new file mode 100644 index 0000000..aed9efc --- /dev/null +++ b/filepicker/src/main/java/me/rosuh/filepicker/utils/DefaultLogger.kt @@ -0,0 +1,30 @@ +package me.rosuh.filepicker.utils + +import android.util.Log +import me.rosuh.filepicker.config.ILog + +internal object DefaultLogger : ILog { + override fun d(tag: String, msg: String) { + Log.d(tag, msg) + } + + override fun e(tag: String, msg: String, tr: Throwable?) { + Log.e(tag, msg, tr) + } + + override fun i(tag: String, msg: String) { + Log.i(tag, msg) + } + + override fun v(tag: String, msg: String) { + Log.v(tag, msg) + } + + override fun w(tag: String, msg: String) { + Log.w(tag, msg) + } + + override fun wtf(tag: String, msg: String) { + Log.wtf(tag, msg) + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8fdbc19..cdd91f7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/sample/build.gradle b/sample/build.gradle index 949fba5..4f73e41 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -2,14 +2,13 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 31 defaultConfig { applicationId "me.rosuh.androidfilepicker" minSdkVersion 19 - targetSdkVersion 31 + compileSdk 33 + targetSdkVersion 33 versionCode 1 versionName "1.0" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' @@ -34,16 +33,16 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation project(':filepicker') - implementation ("com.github.bumptech.glide:glide:4.12.0") { + implementation ("com.github.bumptech.glide:glide:4.14.2") { exclude group: "com.android.support" } // implementation 'com.squareup.picasso:picasso:2.5.2' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.2' - implementation 'androidx.recyclerview:recyclerview:1.2.1' +// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.recyclerview:recyclerview:1.3.2' testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } diff --git a/sample/src/androidTest/java/me/rosuh/sample/ExampleInstrumentedTest.java b/sample/src/androidTest/java/me/rosuh/sample/ExampleInstrumentedTest.java deleted file mode 100644 index e969718..0000000 --- a/sample/src/androidTest/java/me/rosuh/sample/ExampleInstrumentedTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.rosuh.sample; - -import android.content.Context; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("me.rosuh.sample.test", appContext.getPackageName()); - } -} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 8ff8e24..8d0bedc 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,18 +1,32 @@ + package="me.rosuh.sample"> + + + + + + - + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name_file_picker" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + + android:name=".SampleActivity" + android:exported="true"> diff --git a/sample/src/main/java/me/rosuh/sample/SampleActivity.kt b/sample/src/main/java/me/rosuh/sample/SampleActivity.kt index 3c13044..5e52b09 100644 --- a/sample/src/main/java/me/rosuh/sample/SampleActivity.kt +++ b/sample/src/main/java/me/rosuh/sample/SampleActivity.kt @@ -1,14 +1,14 @@ package me.rosuh.sample -//import com.bumptech.glide.Glide +import android.Manifest.permission.READ_EXTERNAL_STORAGE +import android.Manifest.permission.READ_MEDIA_AUDIO +import android.Manifest.permission.READ_MEDIA_IMAGES +import android.Manifest.permission.READ_MEDIA_VIDEO import android.app.Activity import android.content.Context import android.content.Intent -import android.net.Uri import android.os.Build import android.os.Bundle -import android.os.Environment -import android.provider.Settings import android.util.Log import android.view.LayoutInflater import android.view.View @@ -16,15 +16,22 @@ import android.view.ViewGroup import android.widget.Button import android.widget.ImageView import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.demo_activity_main.* import me.rosuh.filepicker.adapter.FileListAdapter import me.rosuh.filepicker.bean.FileItemBeanImpl -import me.rosuh.filepicker.config.* +import me.rosuh.filepicker.config.AbstractFileDetector +import me.rosuh.filepicker.config.AbstractFileFilter +import me.rosuh.filepicker.config.FileItemOnClickListener +import me.rosuh.filepicker.config.FilePickerConfig +import me.rosuh.filepicker.config.FilePickerManager +import me.rosuh.filepicker.config.ItemClickListener +import me.rosuh.filepicker.config.SimpleItemClickListener import me.rosuh.filepicker.engine.ImageEngine import me.rosuh.filepicker.filetype.AudioFileType import me.rosuh.filepicker.filetype.FileType @@ -56,8 +63,20 @@ class SampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.demo_activity_main) + // Register ActivityResult handler + val requestPermissions = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { results -> + // Handle permission requests results + // See the permission example in the Android platform samples: https://github.com/android/platform-samples + } + + // Permission request logic + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO)) + } else { + requestPermissions.launch(arrayOf(READ_EXTERNAL_STORAGE)) + } // 单选 - btn_single.setOnClickListener { + findViewById(R.id.btn_single).setOnClickListener { FilePickerManager .from(this@SampleActivity) .setTheme(getRandomTheme()) @@ -80,7 +99,7 @@ class SampleActivity : AppCompatActivity() { .forResult(FilePickerManager.REQUEST_CODE) } // 只展示文件夹 - btn_only_dir.setOnClickListener { + findViewById(R.id.btn_only_dir).setOnClickListener { FilePickerManager .from(this@SampleActivity) .setTheme(getRandomTheme()) @@ -94,7 +113,7 @@ class SampleActivity : AppCompatActivity() { .forResult(FilePickerManager.REQUEST_CODE) } // 只展示图片 - btn_only_image.setOnClickListener { + findViewById(R.id.btn_only_image).setOnClickListener { FilePickerManager .from(this@SampleActivity) .setTheme(getRandomTheme()) @@ -136,7 +155,7 @@ class SampleActivity : AppCompatActivity() { } // 显示隐藏文件,. 符号开头的 - btn_display_hidden.setOnClickListener { + findViewById(R.id.btn_display_hidden).setOnClickListener { FilePickerManager .from(this@SampleActivity) .setTheme(getRandomTheme()) @@ -145,7 +164,7 @@ class SampleActivity : AppCompatActivity() { } // 单选文件夹 - btn_single_dir.setOnClickListener { + findViewById(R.id.btn_single_dir).setOnClickListener { FilePickerManager .from(this@SampleActivity) .enableSingleChoice() @@ -190,7 +209,7 @@ class SampleActivity : AppCompatActivity() { .forResult(FilePickerManager.REQUEST_CODE) } // 多选文件 - btn_multi_file.setOnClickListener { + findViewById(R.id.btn_multi_file).setOnClickListener { FilePickerManager .from(this@SampleActivity) .setTheme(getRandomTheme()) @@ -198,7 +217,7 @@ class SampleActivity : AppCompatActivity() { .forResult(FilePickerManager.REQUEST_CODE) } // 多选文件夹 - btn_multi_dir.setOnClickListener { + findViewById(R.id.btn_multi_dir).setOnClickListener { FilePickerManager .from(this@SampleActivity) .setTheme(getRandomTheme()) @@ -214,7 +233,7 @@ class SampleActivity : AppCompatActivity() { .forResult(FilePickerManager.REQUEST_CODE) } // 自定义根目录 - btn_custom_root_path.setOnClickListener { + findViewById(R.id.btn_custom_root_path).setOnClickListener { FilePickerManager.from(this@SampleActivity) .storageType("⬇️", FilePickerConfig.STORAGE_CUSTOM_ROOT_PATH) .setTheme(getRandomTheme()) @@ -225,7 +244,7 @@ class SampleActivity : AppCompatActivity() { } // 自定义文件类型 // the new api for register your custom file type - btn_custom_file_type.setOnClickListener { + findViewById(R.id.btn_custom_file_type).setOnClickListener { FilePickerManager.from(this@SampleActivity) .setTheme(getRandomTheme()) .registerFileType(arrayListOf(AudioFileType())) @@ -335,8 +354,9 @@ class SampleActivity : AppCompatActivity() { FilePickerManager.REQUEST_CODE -> { if (resultCode == Activity.RESULT_OK) { val list = FilePickerManager.obtainData(release = true) - rv_main.adapter = SampleAdapter(layoutInflater, ArrayList(list)) - rv_main.layoutManager = + val rv = findViewById(R.id.rv_main) + rv.adapter = SampleAdapter(layoutInflater, ArrayList(list)) + rv.layoutManager = LinearLayoutManager(this@SampleActivity) } else { Toast.makeText(this@SampleActivity, "没有选择图片", Toast.LENGTH_SHORT).show()