Full Code of NFLeo/Matisse-Kotlin for AI

master 0187da2d10f5 cached
197 files
797.4 KB
189.3k tokens
924 symbols
1 requests
Download .txt
Showing preview only (873K chars total). Download the full file or copy to clipboard to get everything.
Repository: NFLeo/Matisse-Kotlin
Branch: master
Commit: 0187da2d10f5
Files: 197
Total size: 797.4 KB

Directory structure:
gitextract_to5l3p13/

├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── leo/
│       │               └── matisse/
│       │                   └── ExampleInstrumentedTest.kt
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── leo/
│       │   │           └── matisse/
│       │   │               ├── ExampleActivity.kt
│       │   │               ├── FrescoEngine.kt
│       │   │               ├── Glide4Engine.kt
│       │   │               ├── GlideEngine.kt
│       │   │               ├── ImageSizeFilter.kt
│       │   │               ├── MainActivity.kt
│       │   │               └── SizeFilter.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   └── ic_launcher_background.xml
│       │       ├── drawable-v24/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── drawable-xhdpi/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── layout/
│       │       │   ├── activity_example.xml
│       │       │   └── activity_main.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   ├── colors_dracula.xml
│       │       │   ├── ids.xml
│       │       │   ├── strings.xml
│       │       │   └── styles.xml
│       │       └── xml/
│       │           └── file_paths_public.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── leo/
│                       └── matisse/
│                           └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
├── matisse/
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── matisse/
│       │               └── ExampleInstrumentedTest.java
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── matisse/
│       │   │           ├── Matisse.kt
│       │   │           ├── MimeType.kt
│       │   │           ├── MimeTypeManager.kt
│       │   │           ├── SelectionCreator.kt
│       │   │           ├── engine/
│       │   │           │   └── ImageEngine.kt
│       │   │           ├── entity/
│       │   │           │   ├── Album.kt
│       │   │           │   ├── CaptureStrategy.kt
│       │   │           │   ├── ConstValue.kt
│       │   │           │   ├── IncapableCause.kt
│       │   │           │   └── Item.kt
│       │   │           ├── filter/
│       │   │           │   └── Filter.kt
│       │   │           ├── internal/
│       │   │           │   └── entity/
│       │   │           │       └── SelectionSpec.kt
│       │   │           ├── listener/
│       │   │           │   ├── OnCheckedListener.kt
│       │   │           │   └── OnSelectedListener.kt
│       │   │           ├── loader/
│       │   │           │   ├── AlbumLoader.kt
│       │   │           │   └── AlbumMediaLoader.kt
│       │   │           ├── model/
│       │   │           │   ├── AlbumCallbacks.kt
│       │   │           │   ├── AlbumCollection.kt
│       │   │           │   ├── AlbumMediaCollection.kt
│       │   │           │   └── SelectedItemCollection.kt
│       │   │           ├── photoview/
│       │   │           │   ├── Compat.java
│       │   │           │   ├── CustomGestureDetector.java
│       │   │           │   ├── OnGestureListener.java
│       │   │           │   ├── OnMatrixChangedListener.java
│       │   │           │   ├── OnOutsidePhotoTapListener.java
│       │   │           │   ├── OnPhotoTapListener.java
│       │   │           │   ├── OnScaleChangedListener.java
│       │   │           │   ├── OnSingleFlingListener.java
│       │   │           │   ├── OnViewDragListener.java
│       │   │           │   ├── OnViewTapListener.java
│       │   │           │   ├── PhotoView.java
│       │   │           │   ├── PhotoViewAttacher.java
│       │   │           │   └── Util.java
│       │   │           ├── ucrop/
│       │   │           │   ├── PictureMultiCuttingActivity.java
│       │   │           │   ├── PicturePhotoGalleryAdapter.java
│       │   │           │   ├── UCrop.java
│       │   │           │   ├── UCropActivity.java
│       │   │           │   ├── UCropMulti.java
│       │   │           │   ├── callback/
│       │   │           │   │   └── Callback.kt
│       │   │           │   ├── immersion/
│       │   │           │   │   ├── CropImmersiveManage.java
│       │   │           │   │   ├── CropLightStatusBarUtils.java
│       │   │           │   │   └── CropRomUtils.java
│       │   │           │   ├── model/
│       │   │           │   │   ├── AspectRatio.java
│       │   │           │   │   ├── CropParameters.java
│       │   │           │   │   ├── CutInfo.java
│       │   │           │   │   ├── ExifInfo.java
│       │   │           │   │   └── ImageState.java
│       │   │           │   ├── task/
│       │   │           │   │   ├── BitmapCropTask.java
│       │   │           │   │   ├── BitmapLoadShowTask.java
│       │   │           │   │   └── BitmapLoadTask.java
│       │   │           │   ├── util/
│       │   │           │   │   ├── BitmapLoadUtils.java
│       │   │           │   │   ├── CubicEasing.java
│       │   │           │   │   ├── EglUtils.java
│       │   │           │   │   ├── FastBitmapDrawable.java
│       │   │           │   │   ├── FileUtils.java
│       │   │           │   │   ├── ImageHeaderParser.java
│       │   │           │   │   ├── RectUtils.java
│       │   │           │   │   ├── RotationGestureDetector.java
│       │   │           │   │   ├── SelectedStateListDrawable.java
│       │   │           │   │   └── VersionUtils.java
│       │   │           │   └── view/
│       │   │           │       ├── CropImageView.java
│       │   │           │       ├── GestureCropImageView.java
│       │   │           │       ├── OverlayView.java
│       │   │           │       ├── TransformImageView.java
│       │   │           │       ├── UCropView.java
│       │   │           │       └── widget/
│       │   │           │           ├── AspectRatioTextView.java
│       │   │           │           └── HorizontalProgressWheelView.java
│       │   │           ├── ui/
│       │   │           │   ├── activity/
│       │   │           │   │   ├── AlbumPreviewActivity.kt
│       │   │           │   │   ├── BaseActivity.kt
│       │   │           │   │   ├── BasePreviewActivity.kt
│       │   │           │   │   ├── SelectedPreviewActivity.kt
│       │   │           │   │   └── matisse/
│       │   │           │   │       ├── AlbumFolderSheetHelper.kt
│       │   │           │   │       ├── AlbumLoadHelper.kt
│       │   │           │   │       ├── IAlbumLoad.kt
│       │   │           │   │       └── MatisseActivity.kt
│       │   │           │   ├── adapter/
│       │   │           │   │   ├── AlbumMediaAdapter.kt
│       │   │           │   │   ├── FolderItemMediaAdapter.kt
│       │   │           │   │   ├── PicturePreviewPagerAdapter.kt
│       │   │           │   │   ├── PreviewPagerAdapter.kt
│       │   │           │   │   └── RecyclerViewCursorAdapter.kt
│       │   │           │   └── view/
│       │   │           │       ├── BottomSheetDialogFragment.kt
│       │   │           │       ├── FolderBottomSheet.kt
│       │   │           │       ├── MediaSelectionFragment.kt
│       │   │           │       ├── PicturePreviewItemFragment.kt
│       │   │           │       └── PreviewItemFragment.kt
│       │   │           ├── utils/
│       │   │           │   ├── BitmapUtils.kt
│       │   │           │   ├── ExifInterfaceCompat.kt
│       │   │           │   ├── IntentUtils.kt
│       │   │           │   ├── ItemSelectUtils.kt
│       │   │           │   ├── MediaStoreCompat.kt
│       │   │           │   ├── PathUtils.kt
│       │   │           │   ├── PhotoMetadataUtils.kt
│       │   │           │   ├── Platform.kt
│       │   │           │   └── UIUtils.kt
│       │   │           └── widget/
│       │   │               ├── CheckRadioView.kt
│       │   │               ├── CheckView.kt
│       │   │               ├── IncapableDialog.kt
│       │   │               ├── MediaGrid.kt
│       │   │               ├── MediaGridInset.kt
│       │   │               ├── PreviewViewPager.kt
│       │   │               ├── SquareFrameLayout.kt
│       │   │               └── longimage/
│       │   │                   ├── CompatDecoderFactory.java
│       │   │                   ├── DecoderFactory.java
│       │   │                   ├── ImageDecoder.java
│       │   │                   ├── ImageRegionDecoder.java
│       │   │                   ├── ImageSource.java
│       │   │                   ├── ImageViewState.java
│       │   │                   ├── SkiaImageDecoder.java
│       │   │                   ├── SkiaImageRegionDecoder.java
│       │   │                   └── SubsamplingScaleImageView.java
│       │   └── res/
│       │       ├── anim/
│       │       │   ├── bottom_down_out.xml
│       │       │   ├── bottom_up_in.xml
│       │       │   ├── ucrop_anim_fade_in.xml
│       │       │   ├── ucrop_close.xml
│       │       │   ├── ucrop_loader_circle_path.xml
│       │       │   └── ucrop_loader_circle_scale.xml
│       │       ├── color/
│       │       │   ├── selector_base_text.xml
│       │       │   ├── selector_black_text.xml
│       │       │   ├── selector_white_text.xml
│       │       │   └── ucrop_scale_text_view_selector.xml
│       │       ├── drawable/
│       │       │   ├── ucrop_gif_bg.xml
│       │       │   ├── ucrop_oval_true.xml
│       │       │   ├── ucrop_shadow_upside.xml
│       │       │   ├── ucrop_vector_ic_crop.xml
│       │       │   ├── ucrop_vector_loader.xml
│       │       │   └── ucrop_vector_loader_animated.xml
│       │       ├── drawable-xhdpi/
│       │       │   └── transparent.xml
│       │       ├── layout/
│       │       │   ├── activity_matisse.xml
│       │       │   ├── activity_media_preview.xml
│       │       │   ├── dialog_bottom_sheet.xml
│       │       │   ├── dialog_bottom_sheet_folder.xml
│       │       │   ├── fragment_media_selection.xml
│       │       │   ├── fragment_picture_preview_item.xml
│       │       │   ├── fragment_preview_item.xml
│       │       │   ├── include_view_bottom.xml
│       │       │   ├── include_view_navigation.xml
│       │       │   ├── item_album_folder.xml
│       │       │   ├── item_media_grid.xml
│       │       │   ├── item_photo_capture.xml
│       │       │   ├── ucrop_activity_photobox.xml
│       │       │   ├── ucrop_aspect_ratio.xml
│       │       │   ├── ucrop_layout_rotate_wheel.xml
│       │       │   ├── ucrop_layout_scale_wheel.xml
│       │       │   ├── ucrop_picture_activity_multi_cutting.xml
│       │       │   ├── ucrop_picture_gf_adapter_edit_list.xml
│       │       │   ├── ucrop_view.xml
│       │       │   └── view_media_grid_content.xml
│       │       ├── menu/
│       │       │   └── ucrop_menu_activity.xml
│       │       ├── values/
│       │       │   ├── attrs.xml
│       │       │   ├── colors.xml
│       │       │   ├── colors_default.xml
│       │       │   ├── dimens.xml
│       │       │   ├── ids.xml
│       │       │   ├── long_attrs.xml
│       │       │   ├── strings.xml
│       │       │   ├── styles.xml
│       │       │   └── values.xml
│       │       └── values-zh/
│       │           └── strings.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── matisse/
│                       └── ExampleUnitTest.java
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
.DS_Store
.idea
/build
build
/captures
.externalNativeBuild
/gradle


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   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.


================================================
FILE: README.md
================================================
[ ![Download](https://api.bintray.com/packages/nfleo/MatisseKotlin/MatisseKotlin/images/download.svg?version=2.1.1) ](https://bintray.com/nfleo/MatisseKotlin/MatisseKotlin/2.1.1/link)

![Image](/image/banner.png)

[Matisse-kotlin地址](https://github.com/NFLeo/Matisse-Kotlin)[包体大小278kb]()

首先感谢:
Matisse核心功能:[https://github.com/zhihu/Matisse](https://github.com/zhihu/Matisse)

裁剪提供者:Yalantis github地址:[https://github.com/Yalantis/uCrop](https://github.com/Yalantis/uCrop) 

完整说明文档:[Android 图片选择库 MatisseKotlin 版](https://www.jianshu.com/p/ca1e7460fa69)

# 版本更新记录

2020-4-2 (v_2.1.1)
1. 修复裁剪结果无法带回问题

2020-4-2
1. 修复部分设备 column '_data' does not exist

2020-3-30  注:升级后,旧裁剪无法使用
1. 裁剪适配Android Q,实现为UCrop
    裁剪功能 主要暴露两个开关 isCrop(boolean)、isCircleCrop(boolean)
2. 修复部分华为设备图片无法裁剪问题
3. 修复预览大概率ANR问题


2020-1-18
1. 裁剪适配Android Q
2. 去除内部压缩-后期将使用接入Luban压缩
3. 修改提示方式
    setNoticeConsumer { context, noticeType, title, message ->
                             showToast(context, noticeType, title, message)
                         }
4. 修改状态栏处理方法

    setStatusBarFuture { params, view ->
                    // 外部设置状态栏
                    ImmersionBar.with(params)?.run {
                        statusBarDarkFont(true)
                        view?.apply { titleBar(this) }
                        init()
                    }

                    // 外部可隐藏Matisse界面中的标题栏
                    // view?.visibility = if (isDarkStatus) View.VISIBLE else View.GONE
                }
5. 注意: 单独调用拍照走裁剪时,也需要创建SelectionCreator

2020-1-17 (v_2.0.5)
1. 修复Gif图预览崩溃问题

2020-1-14 (v_2.0.5)
1. 拍照、展示图片适配Android Q

2020-1-3 (2.0.3)
1. 修复PreviewPageAdapter获取资源数组越界

2019-12-30
1. 默认关闭内部压缩
2. 修复MatisseActivity中albumLoad空指针异常
3. 修复Item创建时,部分cursor为空问题
4. 修复demo压缩开关出错问题

2019-12-23
1. 修复Gif图片类型无法正确判断问题
2. 修复部分设备第一次拍照后,拍照结果不显示问题

2019-12-19
1. 主题属性命名规范化 见R.style.CustomMatisseStyle
2. MimeTypeManager类新增ofMotionlessImage()静态图类型
3. Item列宽(spanSize和gridExceptedSize)添加限制,避免过大/过小
4. 去除app_name
5. 消除选中闪烁
6. 精简压缩库大小 目前release aar 278k

2019-12-10 (2.0.2)
1. 修复mimeType为空情况
2. 修复spanSize和gridExceptedSize同时设置冲突
    注:同时设置时,读取gridExceptedSize值

2019-11-10 (2.0.1)
1. 修复裁剪结果尺寸异常

2019-11-4 (2.0)
1. 相机单独提取
2. 支持默认选中,可传入上次选中的项(通过图片cursor id或uri string对比)
    注:不支持裁剪带回的图片,裁剪带回的图片无id和uri
```
.setLastChoosePicturesIdOrUri(selectedPathIds as ArrayList<String>?)
```
3. 修复压缩为空带回崩溃

2019-10-29 (1.2.3)
1. 修复相册弹窗高度不准确问题
2. 支持压缩配置,外部添加开关  api:[isInnerCompress]
3. 完善未选中资源时各按钮点击添加提示
4. 修复不同设备返回的媒体类型表示不一致(如:JPEG image/jpeg)
5. 去除[api setStatusIsDark], 外部处理状态栏,见[api setStatusBarFuture]

2019-10-28 (持续更新 待发布)
1. 支持相机拍照完成后多选
2. 扩展提示方法,支持使用外部自定义弹窗
3. 支持外部处理状态栏,去除项目中原[ImmersionBar]库
```
.setStatusBarFuture(object : MFunction<BaseActivity> {
                override fun accept(params: BaseActivity, view: View?) {
                    // 外部设置状态栏
                    ImmersionBar.with(params)?.run {
                        statusBarDarkFont(isDarkStatus)
                        view?.apply { titleBar(this) }
                        init()
                    }

                    // 外部可隐藏Matisse界面中的View
                    view?.visibility = if (isDarkStatus) View.VISIBLE else View.GONE
                }
            })
```
4. 按官方方式适配Android Q

2019-10-21 (1.2.2)
1. 修复方形裁剪图片变形问题
2. 优化单选/多选刷新问题

2019-10-18 (1.2.0)
1. 迁移到androidx
2. 修复并支持图片与视频混合选择
```
设置选择单一类型媒体,示例如下
Matisse.choose(MimeTypeManager.ofAll())
                .maxSelectable(3)
或者
Matisse.choose(MimeTypeManager.ofAll(), true)
                .maxSelectable(3)

设置选择混合类型媒体,示例如下
Matisse.choose(MimeTypeManager.ofAll(), false)
                .maxSelectablePerMediaType(4, 2)

说明:
mediaTypeExclusive true 单一媒体类型选择
     读取maxSelectable属性作为最大值
mediaTypeExclusive false
     读取maxImageSelectable和maxVideoSelectable属性分别作为最大值
```
3. 修改单/多选逻辑
	* 单选支持重新选定,不支持计数方式
	* 多选不支持重新选定,选满外部给出提示方式,支持计数与选中方式
4. 提示方式外部实现
```
SelectionCreator.setNoticeEvent( { context: Context, noticeType: Int, title: String, message: String
                                 ->
                                     // 外部提示,可外部定义样式
                                     showToast(context, noticeType, title, message)
                                 }
                             })
```

2019-10-16
1. 完善主题扩展,并提供图片说明


# Matisse
本项目为知乎原项目kotlin改写版本(2018/9月版本),由于项目纯图片选择库与[Matisse](https://github.com/zhihu/Matisse)UI风格有较大差异,为方便个人使用顺手便对[Matisse](https://github.com/zhihu/Matisse)进行Kotlin翻译,主要对原项目进行部分UI层面改写、已发现bug的修改、新功能添加。
*主要修改内容为:*
```
1. 优化相册选择。
2. 优化单选策略。
3. 添加圆形与方形裁剪。
4. 图片选择后压缩,不失真条件下高比率压缩。
5. 增加主题修改,基本可保证定制成与自身项目风格一致
6. 支持设置状态栏颜色 需依赖[ImmersionBar](https://github.com/gyf-dev/ImmersionBar)
    1.2.2之后版本去除内部ImmersionBar处理


* 注:裁剪成功后只返回裁剪后图片的绝对路径,不返回Uri,需自行转换

具体调用查看 SelectionCreator.java

关于打包报错问题:

使用:
1. gradle中添加 implementation 'com.nfleo:MatisseKotlin:2.0.4'
2. AndroidManifest.xml中添加以下代码
* 注:注意provider androidx的差别
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths_public"/>
        </provider>

3. 6.0+需处理权限
The library requires two permissions:
    - `android.permission.READ_EXTERNAL_STORAGE`
    - `android.permission.WRITE_EXTERNAL_STORAGE`

4. 为适配7.0,,项目manifest的privider标签下 paths文件中添加
    文件名称为file_paths_public(名字随意取,但需与AndroidManifest.xml中引用保持一致)
    <external-path  name="my_images" path="Pictures"/>


| Default Style                  | Other Style Preview                  | Preview                          |
|:------------------------------:|:---------------------------------:|:--------------------------------:|
|![](image/screenshot_default.jpg) | ![](image/screenshot_other.jpg) | ![](image/screenshot_preview.jpg)|

| Circle Crop                    | Square Crop                       |
|:------------------------------:|:---------------------------------:|
|![](image/screenshot_circlecrop.jpg) | ![](image/screenshot_squarecrop.jpg) |


#### Simple usage snippet
------

### 配置主题.
| Media theme                    | Preview theme                       |
|:------------------------------:|:---------------------------------:|
|![](image/media_theme_describe.png) | ![](image/preview_theme_describe.png) |


使用套路与原项目一致,只是多增加了一些参数,另外定制时需配置所提供的所有参数。
使用主题时可直接使用Matisse.Default, 或者在自己项目中另写style继承自Matisse.Default,修改自己所需属性,如下
```
app/style.xml
    <style name="CustomMatisseStyle" parent="Matisse.Default">
        <item name="Media_Back_text">@string/back</item>
        <item name="Media_Sure_text">@string/sure</item>
        <item name="Media_Preview_text">@string/preview</item>
        <item name="Media_Original_text">@string/original</item>
        <item name="Media_Album_text">@string/album</item>
        <item name="Media_Camera_text">@string/camera</item>
    </style>
```

```
matisse/style.xml
    <style name="Matisse.Default" parent="Theme.AppCompat.Light.NoActionBar">
        <!--状态栏相关-->
        <item name="colorPrimary">@color/primary</item>
        <item name="colorPrimaryDark">@color/primary_dark</item>

        <!--顶部导航条高度-->
        <item name="Navigation_height">@dimen/navigation_height</item>
        <!--顶部导航条背景色-->
        <item name="Navigation_bg">@color/navigation_bg</item>
        <!--顶部导航条返回按钮资源图-->
        <item name="Navigation_backRes">@drawable/icon_arrow_right_white</item>
        <!--图片预览界面背景色-->
        <item name="Preview_bg">@color/preview_bg</item>

        <!--底部工具栏背景色-->
        <item name="BottomToolbar_bg">@color/bottomTool_bg</item>
        <!--底部工具栏高度-->
        <item name="BottomToolbar_height">@dimen/bottom_tool_height</item>

        <!--返回按钮文字 当无文字是可设置为空字符串 如下-->
        <item name="Media_Back_text">@string/button_null</item>
        <!--返回按钮颜色-->
        <item name="Media_Back_textColor">@color/color_base</item>
        <!--返回按钮文字大小-->
        <item name="Media_Back_textSize">@dimen/text_back</item>

        <item name="Media_Sure_text">@string/button_sure</item>
        <!--确定按钮颜色-->
        <item name="Media_Sure_textColor">@color/color_base</item>
        <!--确认按钮文字大小-->
        <item name="Media_Sure_textSize">@dimen/text_sure</item>

        <item name="Media_Preview_text">@string/button_preview</item>
        <!--预览按钮颜色-->
        <item name="Media_Preview_textColor">@color/text_preview</item>
        <!--预览按钮文字大小-->
        <item name="Media_Preview_textSize">@dimen/text_preview</item>

        <item name="Media_Original_text">@string/button_original</item>
        <!--原图选择控件文字颜色-->
        <item name="Media_Original_textColor">@color/text_original</item>
        <!--原图按钮文字大小-->
        <item name="Media_Original_textSize">@dimen/text_original</item>

        <item name="Media_Album_text">@string/album_name_all</item>
        <!--查看全部相册文件夹按钮颜色-->
        <item name="Media_Album_textColor">@color/text_album</item>
        <!--查看全部相册按钮文字大小-->
        <item name="Media_Album_textSize">@dimen/text_album</item>
        <!--相册文件夹内部列表item颜色-->
        <item name="Media_Album_Item_textColor">@color/text_item_album</item>
        <!--查看全部相册内item文字大小-->
        <item name="Media_Album_Item_textSize">@dimen/text_item_album</item>

        <!--列表中可拍照状态下相机item文字颜色-->
        <item name="Media_Camera_textColor">@color/text_camera</item>
        <!--列表中可拍照状态下相机item文字大小-->
        <item name="Media_Camera_textSize">@dimen/text_camera</item>

        <item name="Preview_Back_text">@string/button_back</item>
        <item name="Preview_Confirm_text">@string/button_sure_default</item>

        <!--选中控件背景色-->
        <item name="Item_checkCircle_bgColor">@color/selector_base_text</item>
        <!--选中控件圆环边框颜色-->
        <item name="Item_checkCircle_borderColor">@color/item_checkCircle_borderColor</item>
        <!--原图radio控件颜色-->
        <item name="Item_checkRadio">@color/selector_base_text</item>

        <!--空状态文字颜色-->
        <item name="Media_Empty_textColor">@color/text_empty</item>
        <!--空状态文字大小-->
        <item name="Media_Empty_textSize">@dimen/text_empty</item>
        <item name="Media_Empty_text">@string/empty_text</item>
        <!--空状态资源图-->
        <item name="Media_Empty_resource">@drawable/ic_empty_zhihu</item>

        <!--图片加载占位图-->
        <!--<item name="Item_placeholder">@drawable/zhihu_item_placeholder</item>-->
    </style>

如需定制UI样式 按需修改,否则使用上述默认主题
Matisse.from(this@MainActivity)
             .setStatusIsDark(true)   // 按需设置状态栏文字颜色
             .theme(R.style.Matisse_Default)    // 设置成所需主题
```

### 注意:1.1.1版本支持外部设置设置状态栏颜色,关于状态栏与状态栏底色问题
##### 项目内部 仅添加了该库(`compileOnly 'com.gyf.barlibrary:barlibrary:2.3.0'`)的编译
需要根据样式修改状态栏文字颜色,图片预览界面状态栏隐藏功能,需在自己项目中引入该库
```
Matisse.from(this@MainActivity)
             ...
             .setStatusIsDark(true)   // 设置状态栏文字颜色 true=黑色  false=白色
             .theme(R.style.Matisse_Default)    // 设置成所需主题
             ...
```

Start `MatisseActivity` from current `Activity` or `Fragment`:

```
kotlin项目调用
Matisse.from(MainActivity.this)
        .choose(MimeTypeManager.ofAll(), false)
        .countable(true)
        .maxSelectable(9)
        .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
        .gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
        .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
        .thumbnailScale(0.85f)
        .imageEngine(new GlideEngine())
        .forResult(REQUEST_CODE_CHOOSE);

java项目调用
Matisse.Companion.from(MainActivity.this)
        .choose(MimeTypeManager.Companion.ofAll(), false)
        .countable(true)
        .maxSelectable(9)
        .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
        .gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
        .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
        .thumbnailScale(0.85f)
        .imageEngine(new GlideEngine())
        .forResult(REQUEST_CODE_CHOOSE);

Matisse.from(SampleActivity.this)
        .choose(MimeTypeManager.ofAll(), false)      // 展示所有类型文件(图片 视频 gif)
        .capture(true)                        // 可拍照
        .countable(true)                      // 记录文件选择顺序
        .captureStrategy(new CaptureStrategy(true, "cache path"))
        .maxSelectable(1)                     // 最多选择一张
        .isCrop(true)                         // 开启裁剪
        .isCircleCrop(true)                   // 设置裁剪类型
        .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))  // 筛选数据源可选大小限制
        .gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
        .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
        .thumbnailScale(0.8f)
        .setStatusIsDark(true)            // 设置状态栏文字颜色 需依赖ImmersionBar库
        .imageEngine(new GlideEngine())   // 加载库需外部实现
        .forResult(REQUEST_CODE_CHOOSE);
```

为方便后期兼容Fresco,图片加载类需外部实现
**注意:**目前慎用Fresco(尽管提供了栗子)!!!,图片加载兼容了Fresco,***但,图片放大预览并未兼容***
```
class Glide4Engine : ImageEngine {

    override fun cleanMemory(context: Context) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            Glide.get(context).clearMemory()
        }
    }

    override fun pause(context: Context) {
        Glide.with(context).pauseRequests()
    }

    override fun resume(context: Context) {
        Glide.with(context).resumeRequests()
    }

    override fun init(context: Context) {
    }

    override fun loadThumbnail(context: Context, resize: Int, placeholder: Drawable, imageView: ImageView, uri: Uri) {
        Glide.with(context)
                .asBitmap() // some .jpeg files are actually gif
                .load(uri)
                .apply(RequestOptions()
                        .override(resize, resize)
                        .placeholder(placeholder)
                        .centerCrop())
                .into(imageView)
    }

    override fun loadGifThumbnail(context: Context, resize: Int, placeholder: Drawable, imageView: ImageView,
                                  uri: Uri) {
        Glide.with(context)
                .asBitmap() // some .jpeg files are actually gif
                .load(uri)
                .apply(RequestOptions()
                        .override(resize, resize)
                        .placeholder(placeholder)
                        .centerCrop())
                .into(imageView)
    }

    override fun loadImage(context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri) {
        Glide.with(context)
                .load(uri)
                .apply(RequestOptions()
                        .override(resizeX, resizeY)
                        .priority(Priority.HIGH)
                        .fitCenter())
                .into(imageView)
    }

    override fun loadGifImage(context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri) {
        Glide.with(context)
                .asGif()
                .load(uri)
                .apply(RequestOptions()
                        .override(resizeX, resizeY)
                        .priority(Priority.HIGH)
                        .fitCenter())
                .into(imageView)
    }
}
```

================================================
FILE: app/.gitignore
================================================
/build


================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion

        applicationId "com.leo.matisse"
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility 1.8
        targetCompatibility 1.8
    }
}

repositories {
    flatDir {
        dirs 'libs'
    }
}

/**
 * @Parcelize 注解方式显示序列化
 * */
androidExtensions {
    experimental = true
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation "com.github.bumptech.glide:glide:$rootProject.ext.glide"
    implementation 'com.google.android.material:material:1.0.0'
    /*动态权限*/
    implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar'
    implementation 'io.reactivex.rxjava2:rxjava:2.2.12'
    implementation 'com.facebook.fresco:fresco:1.14.1'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.gyf.barlibrary:barlibrary:2.3.0'

//    implementation project(':matisse')
    implementation 'com.nfleo:MatisseKotlin:2.1.1'
}


================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


================================================
FILE: app/src/androidTest/java/com/leo/matisse/ExampleInstrumentedTest.kt
================================================
package com.leo.matisse

import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
 * Instrumented test, which will execute on an Android device.
 *
 * See [testing documentation](http://d.android.com/tools/testing).
 */
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @Test
    fun useAppContext() {
        // Context of the app under test.
        val appContext = InstrumentationRegistry.getTargetContext()
        assertEquals("com.leo.matisse", appContext.packageName)
    }
}


================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.leo.matisse">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".ExampleActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>
        <activity android:name=".MainActivity" />

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths_public"/>
        </provider>

    </application>

</manifest>

================================================
FILE: app/src/main/java/com/leo/matisse/ExampleActivity.kt
================================================
package com.leo.matisse

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.CompoundButton
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import com.gyf.barlibrary.ImmersionBar
import com.matisse.Matisse
import com.matisse.MimeType
import com.matisse.MimeTypeManager
import com.matisse.SelectionCreator
import com.matisse.entity.CaptureStrategy
import com.matisse.entity.ConstValue
import com.matisse.entity.IncapableCause
import com.matisse.ui.activity.BaseActivity
import com.matisse.utils.MediaStoreCompat
import com.matisse.utils.Platform
import com.matisse.utils.gotoImageCrop
import com.matisse.widget.IncapableDialog
import com.tbruyelle.rxpermissions2.RxPermissions
import kotlinx.android.synthetic.main.activity_example.*
import java.util.*

class ExampleActivity : AppCompatActivity(), View.OnClickListener {
    private var showType = MimeTypeManager.ofAll()
    private var showCustomizeType: MutableList<MimeType>? = null
    private var mediaTypeExclusive = true
    private var isSingleChoose = false
    private var isCountable = true
    private var defaultTheme = R.style.Matisse_Default
    private var maxCount = 5
    private var maxImageCount = 1
    private var maxVideoCount = 1
    private var isOpenCamera = false
    private var spanCount = 3
    private var gridSizePx = 0
    private var isCrop = false
    private var isCircleCrop = false

    private var isColumnNum = true

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_example)
        initListener()
    }

    private fun initListener() {
        rg_show.setOnCheckedChangeListener { _, checkedId ->
            when (checkedId) {
                R.id.btnAll -> showType = MimeTypeManager.ofAll()
                R.id.btnVideo -> showType = MimeTypeManager.ofVideo()
                R.id.btnImage -> showType = MimeTypeManager.ofImage()
            }
        }

        rg_media_exclusive.setOnCheckedChangeListener { _, checkedId ->
            when (checkedId) {
                R.id.btnMixed -> {
                    mediaTypeExclusive = false
                    ev_max_1.visibility = View.VISIBLE
                    ev_max_2.visibility = if (isSingleChoose) View.GONE else View.VISIBLE
                    tv_max_1.text = "图片最大选择数"
                }
                R.id.btnExclusive -> {
                    mediaTypeExclusive = true
                    ev_max_1.visibility = View.VISIBLE
                    ev_max_2.visibility = View.GONE
                    tv_max_1.text = "最大可选择数"
                }
            }
        }

        rg_theme.setOnCheckedChangeListener { _, checkedId ->
            when (checkedId) {
                R.id.btn_normal_theme -> defaultTheme = R.style.Matisse_Default
                R.id.btn_customize_theme -> defaultTheme = R.style.CustomMatisseStyle
                R.id.btn_jc_theme -> defaultTheme = R.style.JCStyle
            }
        }

        rg_column.setOnCheckedChangeListener { _, checkedId ->
            when (checkedId) {
                R.id.btn_num_column -> {
                    isColumnNum = true
                    ev_column.setText("3")
                }
                R.id.btn_size_column -> {
                    isColumnNum = false
                    ev_column.setText("300")
                }
            }
        }

        if (showCustomizeType != null && showCustomizeType?.size ?: 0 > 0) {
            showType =
                MimeTypeManager.of(showCustomizeType!![0], showCustomizeType?.toTypedArray()!!)
        }

        switch_choose_type.setOnCheckedChangeListener { _, isChecked ->
            isSingleChoose = isChecked
            if (isSingleChoose) {
                maxCount = 1
                maxImageCount = 1
                maxVideoCount = 1
                ev_max_1.visibility = View.GONE
                ev_max_2.visibility = View.GONE

                // 单选才支持裁剪
                ll_crop.visibility = View.VISIBLE
            } else {
                if (mediaTypeExclusive) {
                    tv_max_1.text = "最大可选择数"
                    ev_max_2.visibility = View.GONE
                    ev_max_1.visibility = View.VISIBLE
                } else {
                    tv_max_1.text = "图片最大选择数"
                    ev_max_1.visibility = View.VISIBLE
                    ev_max_2.visibility = View.VISIBLE
                }

                ll_crop.visibility = View.GONE
            }
        }

        switch_check_type.setOnCheckedChangeListener { _, isChecked -> isCountable = !isChecked }

        switch_capture.setOnCheckedChangeListener { _, isChecked -> isOpenCamera = isChecked }

        switch_crop.setOnCheckedChangeListener { _, isChecked ->
            isCrop = isChecked

            if (isChecked) {
                ll_crop_type.visibility = View.VISIBLE
            } else {
                ll_crop_type.visibility = View.GONE
            }
        }

        switch_crop_type.setOnCheckedChangeListener { _, isChecked -> isCircleCrop = isChecked }

        chb_jpeg.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_png.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_gif.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_bmp.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_webp.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_mpeg.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_mp4.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_quick_time.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_threegpp.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_threegpp2.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_mkv.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_webm.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_ts.setOnCheckedChangeListener(checkedOnCheckedListener)
        chb_avi.setOnCheckedChangeListener(checkedOnCheckedListener)

        btn_open_matisse.setOnClickListener(this)
        btn_open_capture.setOnClickListener(this)
    }

    @SuppressLint("CheckResult")
    override fun onClick(v: View?) {

        RxPermissions(this@ExampleActivity)
            .request(
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.CAMERA,
                Manifest.permission.READ_EXTERNAL_STORAGE
            )
            .subscribe {
                if (!it) {
                    showToast(
                        this, IncapableCause.TOAST, "",
                        getString(R.string.permission_request_denied)
                    )
                    return@subscribe
                }

                createMatisse()

                when (v) {
                    btn_open_matisse -> {
                        openMatisse()
                    }
                    btn_open_capture -> {
                        createMediaStoreCompat()
                        mediaStoreCompat?.dispatchCaptureIntent(
                            this, ConstValue.REQUEST_CODE_CAPTURE
                        )
                    }
                }
            }
    }

    private fun showToast(context: Context, noticeType: Int, title: String, message: String) {
        if (noticeType == IncapableCause.TOAST) {
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
        } else if (noticeType == IncapableCause.DIALOG) {
            // 外部弹窗,可外部定义样式
            val incapableDialog = IncapableDialog.newInstance(title, message)
            incapableDialog.show(
                (context as BaseActivity).supportFragmentManager, IncapableDialog::class.java.name
            )
        } else if(noticeType == IncapableCause.LOADING) {

        }

    }

    private var checkedOnCheckedListener =
        CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
            val mimeType = when (buttonView) {
                chb_jpeg -> MimeType.JPEG
                chb_png -> MimeType.PNG
                chb_gif -> MimeType.GIF
                chb_bmp -> MimeType.BMP
                chb_webp -> MimeType.WEBP
                chb_mpeg -> MimeType.MPEG
                chb_mp4 -> MimeType.MP4
                chb_quick_time -> MimeType.QUICKTIME
                chb_threegpp -> MimeType.THREEGPP
                chb_threegpp2 -> MimeType.THREEGPP2
                chb_mkv -> MimeType.MKV
                chb_webm -> MimeType.WEBM
                chb_ts -> MimeType.TS
                chb_avi -> MimeType.AVI
                else -> null
            } ?: return@OnCheckedChangeListener

            if (showCustomizeType == null)
                showCustomizeType = mutableListOf()

            if (isChecked) {
                showCustomizeType?.add(mimeType)
            } else {
                showCustomizeType?.remove(mimeType)
            }
        }

    private var mediaStoreCompat: MediaStoreCompat? = null
    private var selectionCreator: SelectionCreator? = null
    private var selectedPathIds: List<String>? = null

    private fun createMatisse() {

        setEditText()
        selectionCreator =
            Matisse.from(this@ExampleActivity)                              // 绑定Activity/Fragment
                .choose(
                    showType,
                    mediaTypeExclusive
                )                               // 设置显示类型,单一/混合选择模式
                .theme(defaultTheme)                                                // 外部设置主题样式
                .countable(isCountable)                                             // 设置选中计数方式
                .isCrop(isCrop)                                                     // 设置开启裁剪
                .isCircleCrop(isCircleCrop)                                                // 裁剪类型,圆形/方形
                .maxSelectable(maxCount)                                            // 单一选择下 最大选择数量
                .maxSelectablePerMediaType(
                    maxImageCount,
                    maxVideoCount
                )            // 混合选择下 视频/图片最大选择数量
                .capture(isOpenCamera)                                              // 是否开启内部拍摄
                .captureStrategy(                                                   // 拍照设置Strategy
                    CaptureStrategy(
                        true,
                        "${Platform.getPackageName(this@ExampleActivity)}.fileprovider"
                    )
                )
                .thumbnailScale(0.6f)                                         // 图片显示压缩比
                .spanCount(spanCount)                                               // 资源显示列数
                .gridExpectedSize(gridSizePx)                                       // 资源显示网格列宽度
                .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)      // 强制屏幕方向
                .imageEngine(Glide4Engine())                                        // 图片加载实现方式
                .setLastChoosePicturesIdOrUri(selectedPathIds as ArrayList<String>?)// 预选中
                .setNoticeConsumer { context, noticeType, title, message ->
                    showToast(context, noticeType, title, message)
                }.setStatusBarFuture { params, view ->
                    // 外部设置状态栏
                    ImmersionBar.with(params)?.run {
                        statusBarDarkFont(true)
                        view?.apply { titleBar(this) }
                        init()
                    }

                    // 外部可隐藏Matisse界面中的标题栏
                    // view?.visibility = if (isDarkStatus) View.VISIBLE else View.GONE
                }
    }

    private fun createMediaStoreCompat() {
        if (mediaStoreCompat != null) return

        val captureStrategy =
            CaptureStrategy(
                true,
                "${Platform.getPackageName(this@ExampleActivity)}.fileprovider"
            )
        mediaStoreCompat = MediaStoreCompat(this, null)
        mediaStoreCompat?.setCaptureStrategy(captureStrategy)
    }

    private fun openMatisse() {
        selectionCreator?.forResult(ConstValue.REQUEST_CODE_CHOOSE)
    }

    private fun setEditText() {
        if (!mediaTypeExclusive) {
            maxImageCount = formatStrTo0(ev_max_1.text.toString())
            maxVideoCount = formatStrTo0(ev_max_2.text.toString())
        } else {
            if (!isSingleChoose) {
                maxCount = formatStrTo0(ev_max_1.text.toString())
            }
        }

        if (isColumnNum) {
            spanCount = formatStrTo0(ev_column.text.toString())
            gridSizePx = 0
        } else {
            gridSizePx = formatStrTo0(ev_column.text.toString())
            spanCount = 0
        }
    }

    private fun formatStrTo0(s: String?): Int {
        if (s == null || s.toString() == "") {
            Toast.makeText(this, "请保证所有输入非0非空,否则崩溃", Toast.LENGTH_SHORT).show()
            return 0
        }
        return s.toString().toInt()
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode != Activity.RESULT_OK) return

        when (requestCode) {
            ConstValue.REQUEST_CODE_CHOOSE -> doActivityResultForChoose(data)
            ConstValue.REQUEST_CODE_CAPTURE -> doActivityResultForCapture()
            ConstValue.REQUEST_CODE_CROP -> doActivityResultForCrop(data)
        }
    }

    private fun doActivityResultForChoose(data: Intent?) {
        if (data == null) return
        // 获取uri返回值  裁剪结果不返回uri
        val uriList = Matisse.obtainResult(data)
        // 获取文件路径返回值
        selectedPathIds = Matisse.obtainPathIdResult(data)

        uriList?.apply {
            Glide.with(this@ExampleActivity).load(this[0]).into(iv_image)
        }

        showPictureResult(uriList, uriList)
    }

    private fun doActivityResultForCapture() {
        mediaStoreCompat?.getCurrentPhotoUri()?.apply {
            if (isCrop) {
                gotoImageCrop(this@ExampleActivity, arrayListOf(this))
            } else {
                showCompressedPath(this)
            }
        }
    }

    private fun doActivityResultForCrop(data: Intent?) {
        data?.run {
            Matisse.obtainCropResult(data)?.let {
                showCompressedPath(it)
            }
        }
    }

    private fun showCompressedPath(path: Uri) {
        showPictureResult(null, arrayListOf(path))
        Glide.with(this).load(path).into(iv_image)
    }

    private fun showPictureResult(
        uriList: List<Uri>?, strList: List<Uri>?
    ) {
        var string = "uri 路径集合:\n"

        uriList?.forEach {
            string += it.toString() + "\n"
        }

        string += "\npath 路径集合:\n"

        strList?.forEach {
            string += it.toString() + "\n"
        }

        text.text = "\n\n$string"
    }
}


================================================
FILE: app/src/main/java/com/leo/matisse/FrescoEngine.kt
================================================
package com.leo.matisse

import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import androidx.core.view.ViewCompat
import android.view.ViewGroup
import android.widget.ImageView
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.generic.GenericDraweeHierarchy
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
import com.facebook.drawee.interfaces.DraweeController
import com.facebook.drawee.view.DraweeHolder
import com.facebook.imagepipeline.common.ResizeOptions
import com.facebook.imagepipeline.request.ImageRequestBuilder
import com.matisse.engine.ImageEngine

/**
 * Describe :
 * Created by Leo on 2018/10/9 on 15:07.
 */
class FrescoEngine : ImageEngine {

    override fun loadThumbnail(
        context: Context, resize: Int, placeholder: Drawable?, imageView: ImageView, uri: Uri?
    ) {
        var hierarchy: GenericDraweeHierarchy? = null
        val hierarchyBuilder =
            GenericDraweeHierarchyBuilder.newInstance(imageView.context.resources)
        var draweeHolder: DraweeHolder<*>? =
            imageView.getTag(R.id.fresco_drawee) as DraweeHolder<*>?

        hierarchyBuilder.placeholderImage = placeholder
        hierarchyBuilder.failureImage = placeholder

        if (hierarchy == null) {
            hierarchy = hierarchyBuilder.build()
        }

        val controllerBuilder =
            Fresco.newDraweeControllerBuilder().setUri(uri).setAutoPlayAnimations(true)

        val imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(uri)
        imageRequestBuilder.resizeOptions = ResizeOptions(resize, resize)

        val request = imageRequestBuilder.build()
        controllerBuilder.imageRequest = request
        val controller: DraweeController

        if (draweeHolder == null) {
            draweeHolder = DraweeHolder.create(hierarchy, context)
            controller = controllerBuilder.build()
        } else {
            controller = controllerBuilder.setOldController(draweeHolder.controller).build()
        }

        draweeHolder?.controller = controller

        if (ViewCompat.isAttachedToWindow(imageView)) {
            draweeHolder?.onAttach()
        }

        imageView.setTag(R.id.fresco_drawee, draweeHolder)
        imageView.setImageDrawable(draweeHolder?.topLevelDrawable)
    }

    override fun loadGifThumbnail(
        context: Context, resize: Int, placeholder: Drawable?, imageView: ImageView, uri: Uri?
    ) {
    }

    override fun loadImage(
        context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri?
    ) {
        var hierarchy: GenericDraweeHierarchy? = null
        val hierarchyBuilder =
            GenericDraweeHierarchyBuilder.newInstance(imageView.context.resources)
        var draweeHolder: DraweeHolder<*>? =
            imageView.getTag(R.id.fresco_drawee) as DraweeHolder<*>?

        if (hierarchy == null) {
            hierarchy = hierarchyBuilder.build()
        }

        val controllerBuilder =
            Fresco.newDraweeControllerBuilder().setUri(uri).setAutoPlayAnimations(true)

        var params: ViewGroup.LayoutParams? = imageView.layoutParams
        if (params == null) {
            params = ViewGroup.LayoutParams(resizeX, resizeY)
        }

        if (params.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            params.width = ViewGroup.LayoutParams.MATCH_PARENT
        }
        if (params.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            params.height = ViewGroup.LayoutParams.MATCH_PARENT
        }

        imageView.layoutParams = params

        val imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(uri)
        val request = imageRequestBuilder.build()
        controllerBuilder.imageRequest = request
        val controller: DraweeController

        if (draweeHolder == null) {
            draweeHolder = DraweeHolder.create(hierarchy, context)
            controller = controllerBuilder.build()
        } else {
            controller = controllerBuilder.setOldController(draweeHolder.controller).build()
        }

        // 请求
        draweeHolder?.controller = controller

        if (ViewCompat.isAttachedToWindow(imageView)) {
            draweeHolder?.onAttach()
        }

        imageView.setTag(R.id.fresco_drawee, draweeHolder)
        imageView.setImageDrawable(draweeHolder?.topLevelDrawable)
    }

    override fun loadGifImage(
        context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri?
    ) {
    }

    override fun cleanMemory(context: Context) {
        Fresco.getImagePipeline().clearMemoryCaches()
    }

    override fun pause(context: Context) {
        Fresco.getImagePipeline().pause()
    }

    override fun resume(context: Context) {
        Fresco.getImagePipeline().resume()
    }

    override fun init(context: Context) {
        Fresco.initialize(context)
    }
}

================================================
FILE: app/src/main/java/com/leo/matisse/Glide4Engine.kt
================================================
package com.leo.matisse

import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Looper
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.Priority
import com.bumptech.glide.request.RequestOptions
import com.matisse.engine.ImageEngine

/**
 * [ImageEngine] implementation using Glide.
 */
class Glide4Engine : ImageEngine {

    override fun cleanMemory(context: Context) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            Glide.get(context).clearMemory()
        }
    }

    override fun pause(context: Context) {
        Glide.with(context).pauseRequests()
    }

    override fun resume(context: Context) {
        Glide.with(context).resumeRequests()
    }

    override fun init(context: Context) {
    }

    override fun loadThumbnail(
        context: Context, resize: Int, placeholder: Drawable?, imageView: ImageView, uri: Uri?
    ) {
        Glide.with(context)
            .asBitmap() // some .jpeg files are actually gif
            .load(uri)
            .apply(
                RequestOptions()
                    .override(resize, resize).dontAnimate()
                    .placeholder(placeholder).centerCrop()
            )
            .into(imageView)
    }

    override fun loadGifThumbnail(
        context: Context, resize: Int, placeholder: Drawable?, imageView: ImageView, uri: Uri?
    ) {
        Glide.with(context)
            .asBitmap() // some .jpeg files are actually gif
            .load(uri)
            .apply(
                RequestOptions().override(resize, resize).placeholder(placeholder).centerCrop()
            )
            .into(imageView)
    }

    override fun loadImage(
        context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri?
    ) {
        Glide.with(context)
            .load(uri)
            .apply(
                RequestOptions()
                    .override(resizeX, resizeY).priority(Priority.HIGH).fitCenter()
            )
            .into(imageView)
    }

    override fun loadGifImage(
        context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri?
    ) {
        Glide.with(context)
            .asGif()
            .load(uri)
            .apply(
                RequestOptions().override(resizeX, resizeY).priority(Priority.HIGH).fitCenter()
            )
            .into(imageView)
    }
}


================================================
FILE: app/src/main/java/com/leo/matisse/GlideEngine.kt
================================================
package com.leo.matisse

import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Looper
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.Priority
import com.bumptech.glide.request.RequestOptions
import com.matisse.engine.ImageEngine

/**
 * Describe : implementation using Glide.
 * Created by Leo on 2018/9/7 on 10:55.
 */
class GlideEngine : ImageEngine {

    override fun cleanMemory(context: Context) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            Glide.get(context).clearMemory()
        }
    }

    override fun pause(context: Context) {
        Glide.with(context).pauseRequests()
    }

    override fun resume(context: Context) {
        Glide.with(context).resumeRequests()
    }

    override fun init(context: Context) {
    }

    override fun loadThumbnail(
        context: Context, resize: Int, placeholder: Drawable?, imageView: ImageView, uri: Uri?
    ) {
        Glide.with(context)
            .asBitmap()  // some .jpeg files are actually gif
            .load(uri)
            .apply(
                RequestOptions().placeholder(placeholder)
                    .override(resize, resize)
                    .centerCrop()
            )
            .into(imageView)
    }

    override fun loadGifThumbnail(
        context: Context, resize: Int, placeholder: Drawable?, imageView: ImageView, uri: Uri?
    ) {
        Glide.with(context)
            .asBitmap()
            .load(uri)
            .apply(
                RequestOptions().placeholder(placeholder)
                    .override(resize, resize)
                    .centerCrop()
            )
            .into(imageView)
    }

    override fun loadImage(
        context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri?
    ) {
        Glide.with(context)
            .load(uri)
            .apply(
                RequestOptions().priority(Priority.HIGH)
                    .fitCenter()
            )
            .into(imageView)
    }

    override fun loadGifImage(
        context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri?
    ) {
        Glide.with(context)
            .asGif()
            .load(uri)
            .apply(
                RequestOptions().priority(Priority.HIGH)
                    .override(resizeX, resizeY)
            )
            .into(imageView)
    }
}

================================================
FILE: app/src/main/java/com/leo/matisse/ImageSizeFilter.kt
================================================
package com.leo.matisse

import android.content.Context
import com.matisse.MimeTypeManager
import com.matisse.entity.IncapableCause
import com.matisse.entity.Item
import com.matisse.filter.Filter
import com.matisse.utils.PhotoMetadataUtils

/**
 * desc:不允许选择大于itemSize字节的图片</br>
 * time: 2019/10/23-10:12</br>
 * author:Leo </br>
 * since V 1.2.2 </br>
 */
class ImageSizeFilter(private var itemSize: Long) : Filter() {

    // 设置需过滤的资源类型,此处过滤类型为图片
    override fun constraintTypes() = MimeTypeManager.ofImage()

    override fun filter(context: Context, item: Item?): IncapableCause? {
        // 1. 判断当前选中的item是否属于constraintTypes中定义的图片资源
        if (!needFiltering(context, item)) return null

        if (item?.size ?: 0 > itemSize) {
            return IncapableCause(
                IncapableCause.DIALOG, "需选择小于${PhotoMetadataUtils.getSizeInMB(itemSize)}M的图片"
            )
        }

        return null
    }
}

================================================
FILE: app/src/main/java/com/leo/matisse/MainActivity.kt
================================================
package com.leo.matisse

import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import com.bumptech.glide.Glide
import com.matisse.Matisse
import com.matisse.MimeTypeManager
import com.matisse.entity.CaptureStrategy
import com.matisse.entity.ConstValue
import com.matisse.utils.Platform
import com.tbruyelle.rxpermissions2.RxPermissions
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<AppCompatButton>(R.id.btn_media_store).setOnClickListener {
            RxPermissions(this@MainActivity)
                .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
                .subscribe {
                    if (!it) {
                        Toast.makeText(
                            this@MainActivity, R.string.permission_request_denied, Toast.LENGTH_LONG
                        ).show()
                        return@subscribe
                    }
                    openMatisse()
                }
        }
    }

    private fun openMatisse() {
        Matisse.from(this@MainActivity)
            .choose(MimeTypeManager.ofAll())
            .countable(false)
            .capture(true)
            .isCrop(true)
            .isCircleCrop(true)
            .maxSelectable(1)
            .theme(R.style.JCStyle)
            .captureStrategy(
                CaptureStrategy(
                    true,
                    "${Platform.getPackageName(this@MainActivity)}.fileprovider"
                )
            )
            .thumbnailScale(0.8f)
            .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
            .imageEngine(Glide4Engine())
            .forResult(ConstValue.REQUEST_CODE_CHOOSE)
    }

    private fun showToast(value: String) {
        Toast.makeText(this, value, Toast.LENGTH_SHORT).show()
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (data == null) return

        if (requestCode == ConstValue.REQUEST_CODE_CHOOSE && resultCode == Activity.RESULT_OK) {
            var string = ""
            val uriList = Matisse.obtainResult(data) ?: return

            uriList.forEach {
                string += it.toString() + "\n"
            }

            Glide.with(this).load(uriList[0]).into(iv_image)
            string = "\n\n$string"

            text.text = "\n\n$string"
        }
    }
}


================================================
FILE: app/src/main/java/com/leo/matisse/SizeFilter.kt
================================================
package com.leo.matisse

import android.content.Context
import com.matisse.MimeType
import com.matisse.MimeTypeManager
import com.matisse.entity.IncapableCause
import com.matisse.entity.Item
import com.matisse.filter.Filter
import com.matisse.utils.PhotoMetadataUtils

class SizeFilter(private val maxSizeByte: Int) : Filter() {

    override fun constraintTypes(): Set<MimeType> {
        return MimeTypeManager.ofMotionlessImage()
    }

    override fun filter(context: Context, item: Item?): IncapableCause? {
        if (!needFiltering(context, item))
            return null
        return if (item?.size ?: 0 > maxSizeByte) {
            IncapableCause(
                IncapableCause.TOAST,
                "not larger than ${PhotoMetadataUtils.getSizeInMB(maxSizeByte.toLong())} MB"
            )
        } else null
    }
}


================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillColor="#26A69A"
        android:pathData="M0,0h108v108h-108z" />
    <path
        android:fillColor="#00000000"
        android:pathData="M9,0L9,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,0L19,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,0L29,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,0L39,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,0L49,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,0L59,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,0L69,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,0L79,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M89,0L89,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M99,0L99,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,9L108,9"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,19L108,19"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,29L108,29"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,39L108,39"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,49L108,49"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,59L108,59"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,69L108,69"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,79L108,79"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,89L108,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,99L108,99"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,29L89,29"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,39L89,39"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,49L89,49"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,59L89,59"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,69L89,69"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,79L89,79"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,19L29,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,19L39,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,19L49,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,19L59,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,19L69,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,19L79,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
</vector>


================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1" />
</vector>


================================================
FILE: app/src/main/res/drawable-xhdpi/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1" />
</vector>


================================================
FILE: app/src/main/res/layout/activity_example.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="15dp">

        <!--显示资源类型-->
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/tv_show_resource"
                style="@style/style_title_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="5dp"
                android:text="显示资源"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <RadioGroup
                android:id="@+id/rg_show"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="15dp"
                android:orientation="horizontal"
                app:layout_constraintStart_toEndOf="@+id/tv_show_resource"
                app:layout_constraintTop_toTopOf="@+id/tv_show_resource">

                <RadioButton
                    android:id="@+id/btnAll"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:checked="true"
                    android:text="全部资源" />

                <RadioButton
                    android:id="@+id/btnVideo"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="仅视频" />

                <RadioButton
                    android:id="@+id/btnImage"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="仅图片" />
            </RadioGroup>

        </androidx.constraintlayout.widget.ConstraintLayout>

        <!--指定组合类型-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="gone">

            <TextView
                android:id="@+id/tv_show_customize"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="10dp"
                android:text="自定义显示"
                android:textColor="@color/black" />

            <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/view_show_customize"
                android:layout_width="0dp"
                android:layout_height="wrap_content">

                <CheckBox
                    android:id="@+id/chb_jpeg"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="JPEG"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <CheckBox
                    android:id="@+id/chb_png"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="50dp"
                    android:text="PNG"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <CheckBox
                    android:id="@+id/chb_gif"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="GIF"
                    app:layout_constraintStart_toStartOf="@+id/chb_jpeg"
                    app:layout_constraintTop_toBottomOf="@+id/chb_jpeg" />

                <CheckBox
                    android:id="@+id/chb_bmp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="BMP"
                    app:layout_constraintStart_toStartOf="@+id/chb_png"
                    app:layout_constraintTop_toBottomOf="@+id/chb_png" />

                <CheckBox
                    android:id="@+id/chb_webp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="WEBP"
                    app:layout_constraintStart_toStartOf="@+id/chb_jpeg"
                    app:layout_constraintTop_toBottomOf="@+id/chb_gif" />

                <CheckBox
                    android:id="@+id/chb_mpeg"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="MPEG"
                    app:layout_constraintStart_toStartOf="@+id/chb_png"
                    app:layout_constraintTop_toBottomOf="@+id/chb_bmp" />

                <CheckBox
                    android:id="@+id/chb_mp4"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="MP4"
                    app:layout_constraintStart_toStartOf="@+id/chb_jpeg"
                    app:layout_constraintTop_toBottomOf="@+id/chb_webp" />

                <CheckBox
                    android:id="@+id/chb_quick_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="QUICKTIME"
                    app:layout_constraintStart_toStartOf="@+id/chb_png"
                    app:layout_constraintTop_toBottomOf="@+id/chb_mpeg" />

                <CheckBox
                    android:id="@+id/chb_threegpp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="GPP"
                    app:layout_constraintStart_toStartOf="@+id/chb_jpeg"
                    app:layout_constraintTop_toBottomOf="@+id/chb_mp4" />

                <CheckBox
                    android:id="@+id/chb_threegpp2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="GPP2"
                    app:layout_constraintStart_toStartOf="@+id/chb_png"
                    app:layout_constraintTop_toBottomOf="@+id/chb_quick_time" />

                <CheckBox
                    android:id="@+id/chb_mkv"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="MKV"
                    app:layout_constraintStart_toStartOf="@+id/chb_jpeg"
                    app:layout_constraintTop_toBottomOf="@+id/chb_threegpp" />

                <CheckBox
                    android:id="@+id/chb_webm"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="WEBM"
                    app:layout_constraintStart_toStartOf="@+id/chb_png"
                    app:layout_constraintTop_toBottomOf="@+id/chb_threegpp2" />

                <CheckBox
                    android:id="@+id/chb_ts"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="TS"
                    app:layout_constraintStart_toStartOf="@+id/chb_jpeg"
                    app:layout_constraintTop_toBottomOf="@+id/chb_mkv" />

                <CheckBox
                    android:id="@+id/chb_avi"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="AVI"
                    app:layout_constraintStart_toStartOf="@+id/chb_png"
                    app:layout_constraintTop_toBottomOf="@+id/chb_webm" />

            </androidx.constraintlayout.widget.ConstraintLayout>

        </LinearLayout>

        <!--设置选择类型-->
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/tv_media_exclusive"
                style="@style/style_title_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="8dp"
                android:text="混合/单一\n是否可同时\n选择图片和\n视频"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <RadioGroup
                android:id="@+id/rg_media_exclusive"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                app:layout_constraintStart_toEndOf="@+id/tv_media_exclusive"
                app:layout_constraintTop_toTopOf="@+id/tv_media_exclusive">

                <RadioButton
                    android:id="@+id/btnMixed"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="混合选择" />

                <RadioButton
                    android:id="@+id/btnExclusive"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:checked="true"
                    android:text="单一选择" />

            </RadioGroup>

        </androidx.constraintlayout.widget.ConstraintLayout>

        <!--单多选、计数-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_choose_type"
                style="@style/style_title_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="单/多选" />

            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/switch_choose_type"
                android:layout_width="wrap_content"
                android:layout_height="30dp"
                android:layout_marginStart="25dp"
                android:checked="false"
                android:textOff="多选"
                android:textOn="单选"
                android:textSize="10sp"
                app:showText="true" />

            <TextView
                android:id="@+id/tv_check_type"
                style="@style/style_title_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="50dp"
                android:text="是否计数" />

            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/switch_check_type"
                android:layout_width="wrap_content"
                android:layout_height="30dp"
                android:layout_marginStart="10dp"
                android:checked="false"
                android:textOff="是"
                android:textOn="否"
                android:textSize="10sp"
                app:showText="true" />

        </LinearLayout>

        <!--开启拍照、内部压缩-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_capture"
                style="@style/style_title_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="开启拍照" />

            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/switch_capture"
                android:layout_width="wrap_content"
                android:layout_height="30dp"
                android:layout_marginStart="15dp"
                android:checked="false"
                android:textOff="关"
                android:textOn="开"
                android:textSize="10sp"
                app:showText="true" />

        </LinearLayout>

        <!--设置主题-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_theme"
                style="@style/style_title_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="默认主题"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tv_capture" />

            <RadioGroup
                android:id="@+id/rg_theme"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="15dp"
                android:orientation="horizontal"
                app:layout_constraintStart_toStartOf="@+id/rg_show"
                app:layout_constraintTop_toTopOf="@+id/tv_theme">

                <RadioButton
                    android:id="@+id/btn_normal_theme"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:checked="true"
                    android:text="默认主题" />

                <RadioButton
                    android:id="@+id/btn_customize_theme"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="自定义主题" />

                <RadioButton
                    android:id="@+id/btn_jc_theme"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="JC主题" />

            </RadioGroup>

        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_crop"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <!--裁剪-->
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/tv_crop"
                    style="@style/style_title_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="开启裁剪" />

                <androidx.appcompat.widget.SwitchCompat
                    android:id="@+id/switch_crop"
                    android:layout_width="wrap_content"
                    android:layout_height="30dp"
                    android:layout_marginStart="15dp"
                    android:checked="false"
                    android:textOff="关"
                    android:textOn="开"
                    android:textSize="10sp"
                    app:showText="true" />

            </LinearLayout>

            <!--裁剪类型、保存类型-->
            <LinearLayout
                android:id="@+id/ll_crop_type"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/tv_crop_type"
                    style="@style/style_title_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="裁剪类型" />

                <androidx.appcompat.widget.SwitchCompat
                    android:id="@+id/switch_crop_type"
                    android:layout_width="wrap_content"
                    android:layout_height="30dp"
                    android:layout_marginStart="15dp"
                    android:checked="false"
                    android:textOff="方"
                    android:textOn="圆"
                    android:textSize="10sp"
                    app:showText="true" />

            </LinearLayout>

        </LinearLayout>

        <!--设置列数-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_column"
                style="@style/style_title_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="设置列数" />

            <RadioGroup
                android:id="@+id/rg_column"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="15dp"
                android:orientation="horizontal">

                <RadioButton
                    android:id="@+id/btn_num_column"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:checked="true"
                    android:text="列表列数" />

                <RadioButton
                    android:id="@+id/btn_size_column"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="列表宽度" />

            </RadioGroup>

        </LinearLayout>

        <EditText
            android:id="@+id/ev_column"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:inputType="number"
            android:text="3"
            android:textSize="14sp" />

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <EditText
                android:id="@+id/ev_max_1"
                android:layout_width="0dp"
                android:layout_height="50dp"
                android:layout_marginTop="20dp"
                android:inputType="number"
                android:text="3"
                android:textSize="14sp"
                android:visibility="gone"
                app:layout_constraintEnd_toStartOf="@+id/ev_max_2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/tv_max_1"
                style="@style/style_title_text"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:text="最大可选择数"
                app:layout_constraintBottom_toTopOf="@+id/ev_max_1"
                app:layout_constraintEnd_toEndOf="@+id/ev_max_1"
                app:layout_constraintStart_toStartOf="@+id/ev_max_1" />

            <TextView
                android:id="@+id/tv_max_2"
                style="@style/style_title_text"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:text="视频最大选择数"
                app:layout_constraintBottom_toTopOf="@+id/ev_max_2"
                app:layout_constraintEnd_toEndOf="@+id/ev_max_2"
                app:layout_constraintStart_toStartOf="@+id/ev_max_2" />

            <EditText
                android:id="@+id/ev_max_2"
                android:layout_width="0dp"
                android:layout_height="50dp"
                android:inputType="number"
                android:text="1"
                android:textSize="14sp"
                android:visibility="gone"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/ev_max_1"
                app:layout_constraintTop_toTopOf="@+id/ev_max_1"
                tools:visibility="gone" />

        </androidx.constraintlayout.widget.ConstraintLayout>

        <!--操作按钮相册、相机-->
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="80dp">

            <androidx.appcompat.widget.AppCompatButton
                android:id="@+id/btn_open_matisse"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="start"
                android:text="打开相册" />

            <androidx.appcompat.widget.AppCompatButton
                android:id="@+id/btn_open_capture"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="end"
                android:layout_marginStart="50dp"
                android:text="打开相机" />

        </LinearLayout>

        <!--结果显示-->
        <ImageView
            android:id="@+id/iv_image"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:adjustViewBounds="true"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/btn_open_matisse" />

        <TextView
            android:id="@+id/text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="15dp"
            android:lineSpacingExtra="3dp"
            android:textColor="@color/color_title_text"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/iv_image" />

    </LinearLayout>
</androidx.core.widget.NestedScrollView>

================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="wrap_content"
        android:layout_height="150dp"
        android:adjustViewBounds="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn_media_store"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:foreground="?selectableItemBackground"
        android:gravity="center"
        android:text="媒体库"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:lineSpacingExtra="3dp"
        android:textColor="@color/color_title_text"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <color name="preview_bottom_size">#61FFFFFF</color>
</resources>


================================================
FILE: app/src/main/res/values/colors_dracula.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright 2017 Zhihu Inc.

  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.
  -->
<resources>

</resources>

================================================
FILE: app/src/main/res/values/ids.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="fresco_drawee" type="id"></item>
</resources>

================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">MatisseKotlin</string>
    <string name="permission_request_denied">请求读写权限失败!</string>

    <string name="back">Media.Back</string>
    <string name="sure">Sure</string>
    <string name="preview">Media.Preview</string>
    <string name="original">Media.Original</string>
    <string name="camera">Media.Camera</string>
    <string name="album">Media.Album</string>
</resources>


================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="style_title_text">
        <item name="android:textColor">#000000</item>
        <item name="android:textSize">16sp</item>
        <item name="android:textStyle">bold</item>
    </style>

    <style name="CustomMatisseStyle" parent="Matisse.Default">
        <item name="Media_Back_text">@string/back</item>
        <item name="Media_Sure_text">@string/sure</item>
        <item name="Media_Preview_text">@string/preview</item>
        <item name="Media_Original_text">@string/original</item>
        <item name="Media_Album_text">@string/album</item>
        <item name="Media_Camera_text">@string/camera</item>
        <item name="Media_Sure_textColor">#00ff00</item>
        <item name="Media_Album_textColor">#0000ff</item>
        <item name="Media_Preview_textColor">#ff0000</item>
        <item name="Item_placeholder">@drawable/ic_launcher_foreground</item>
    </style>

    <style name="JCStyle" parent="Matisse.Default">
        <item name="colorPrimary">#EB5148</item>
        <item name="colorPrimaryDark">#EB5148</item>
        <item name="Navigation_bg">#EB5148</item>
        <item name="Navigation_backRes">@mipmap/ic_navbar48_chervon_white</item>
        <item name="Media_Sure_text">@string/sure</item>
        <item name="Media_Sure_textColor">@color/selector_white_text</item>
        <item name="Media_Preview_text">@string/preview</item>
        <item name="Media_Album_text">@string/album</item>
        <item name="Media_Camera_text">@string/camera</item>
    </style>
</resources>


================================================
FILE: app/src/main/res/xml/file_paths_public.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="my_images"
        path="Pictures" />
</paths>

================================================
FILE: app/src/test/java/com/leo/matisse/ExampleUnitTest.kt
================================================
package com.leo.matisse

import org.junit.Test

import org.junit.Assert.*

/**
 * Example local unit test, which will execute on the development machine (host).
 *
 * See [testing documentation](http://d.android.com/tools/testing).
 */
class ExampleUnitTest {
    @Test
    fun addition_isCorrect() {
        assertEquals(4, 2 + 2)
    }
}


================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.50'

    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:3.4.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.novoda:bintray-release:0.9.1'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven { url "https://jitpack.io" }
    }
}

ext {
    compileSdkVersion = 29

    minSdkVersion = 19
    targetSdkVersion = 29

    appcompat = '1.0.0'
    material = '1.0.0'
    recyclerview = '1.0.0'
    glide = '4.7.1'
    constraintlayout = '1.1.3'
}

tasks.withType(Javadoc) {
    options.addStringOption('Xdoclint:none', '-quiet')
    options.addStringOption('encoding', 'UTF-8')
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true


================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: matisse/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

apply plugin: 'com.novoda.bintray-release'

Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())

String localBintrayUser = properties.getProperty("bintray.user")
String localBintrayApikey = properties.getProperty("bintray.apikey")

publish {
    bintrayUser = localBintrayUser   //bintray.com用户名
    bintrayKey = localBintrayApikey  //bintray.com apikey
    dryRun = false
    repoName = 'MatisseKotlin'
    userOrg = 'nfleo'//bintray.com用户名
    groupId = 'com.nfleo'//jcenter上的路径
    artifactId = 'MatisseKotlin'//项目名称
    publishVersion = '2.1.1'//版本号
    desc = 'this is for MatisseKotlin'
    website = 'https://github.com/NFLeo/Matisse-Kotlin'
}

android {
    compileSdkVersion rootProject.ext.compileSdkVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        vectorDrawables.useSupportLibrary = true
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    lintOptions {
        abortOnError false
    }

    compileOptions {
        /* Java8支持 */
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8
    }
}

androidExtensions {
    experimental = true
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    /*ImageViewTouch*/
    implementation "it.sephiroth.android.library.imagezoom:library:1.0.4"
    implementation "androidx.appcompat:appcompat:$rootProject.ext.appcompat"
    implementation "com.google.android.material:material:$rootProject.ext.material"
    implementation "androidx.recyclerview:recyclerview:$rootProject.ext.recyclerview"
    implementation "androidx.constraintlayout:constraintlayout:$rootProject.ext.constraintlayout"
}

tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

================================================
FILE: matisse/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


================================================
FILE: matisse/src/androidTest/java/com/matisse/ExampleInstrumentedTest.java
================================================
package com.matisse;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;

/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    @Test
    public void useAppContext() {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getTargetContext();

        assertEquals("com.matisse.test", appContext.getPackageName());
    }
}


================================================
FILE: matisse/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.matisse">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />

    <application>
        <activity android:name=".ui.activity.SelectedPreviewActivity" />
        <activity android:name=".ui.activity.AlbumPreviewActivity" />
        <activity android:name=".ui.activity.matisse.MatisseActivity" />
        <activity android:name=".ucrop.UCropActivity" />
        <activity android:name=".ucrop.PictureMultiCuttingActivity" />
    </application>
</manifest>


================================================
FILE: matisse/src/main/java/com/matisse/Matisse.kt
================================================
package com.matisse

import android.app.Activity
import android.content.Intent
import android.net.Uri
import androidx.fragment.app.Fragment
import com.matisse.entity.ConstValue
import java.lang.ref.WeakReference

/**
 * Entry for Matisse's media selection.
 */
class Matisse(activity: Activity?, fragment: Fragment? = null) {

    companion object {

        /**
         * Start Matisse from an Activity.
         * This Activity's [Activity.onActivityResult] will be called when user
         * finishes selecting.
         *
         * @param activity Activity instance.
         * @return Matisse instance.
         */
        fun from(activity: Activity?): Matisse {
            return Matisse(activity)
        }

        /**
         * Start Matisse from a Fragment.
         *
         * This Fragment's [Fragment.onActivityResult] will be called when user
         * finishes selecting.
         *
         * @param fragment Fragment instance.
         * @return Matisse instance.
         */
        fun from(fragment: Fragment): Matisse {
            return Matisse(fragment)
        }

        /**
         * Obtain user selected media' [Uri] list in the starting Activity or Fragment.
         *
         * @param data Intent passed by [Activity.onActivityResult] or
         * [Fragment.onActivityResult].
         * @return User selected media' [Uri] list.
         */
        fun obtainResult(data: Intent): List<Uri>? {
            return data.getParcelableArrayListExtra(ConstValue.EXTRA_RESULT_SELECTION)
        }

        /**
         * Obtain user selected media path id list in the starting Activity or Fragment.
         *
         * @param data Intent passed by [Activity.onActivityResult] or
         * [Fragment.onActivityResult].
         * @return User selected media path id list.
         */
        fun obtainPathIdResult(data: Intent): List<String>? {
            return data.getStringArrayListExtra(ConstValue.EXTRA_RESULT_SELECTION_ID)
        }

        /**
         * 直接获取裁剪结果
         */
        fun obtainCropResult(data: Intent?): Uri? {
            return data?.getParcelableExtra(ConstValue.EXTRA_RESULT_CROP_BACK_BUNDLE)
        }

        /**
         * Obtain state whether user decide to use selected media in original
         *
         * @param data Intent passed by [Activity.onActivityResult] or
         * [Fragment.onActivityResult].
         * @return Whether use original photo
         */
        fun obtainOriginalState(data: Intent) =
            data.getBooleanExtra(ConstValue.EXTRA_RESULT_ORIGINAL_ENABLE, false)
    }

    private val mContext = WeakReference(activity)
    private val mFragment: WeakReference<Fragment>?

    internal val activity: Activity?
        get() = mContext.get()

    internal val fragment: Fragment?
        get() = mFragment?.get()

    private constructor(fragment: Fragment) : this(fragment.activity, fragment)

    init {
        mFragment = WeakReference<Fragment>(fragment)
    }

    /**
     * MIME types the selection constrains on.
     * Types not included in the set will still be shown in the grid but can't be chosen.
     *
     * @param mimeTypes MIME types set user can choose from.
     * @return [SelectionCreator] to build select specifications.
     * @see MimeType
     *
     * @see SelectionCreator
     */
    fun choose(mimeTypes: Set<MimeType>): SelectionCreator {
        return this.choose(mimeTypes, true)
    }

    /**
     * MIME types the selection constrains on.
     * Types not included in the set will still be shown in the grid but can't be chosen.
     *
     * @param mimeTypes          MIME types set user can choose from.
     * @param mediaTypeExclusive Whether can choose images and videos at the same time during one single choosing
     * process. true corresponds to not being able to choose images and videos at the same
     * time, and false corresponds to being able to do this.
     * @return [SelectionCreator] to build select specifications.
     * @see MimeType
     *
     * @see SelectionCreator
     */
    fun choose(mimeTypes: Set<MimeType>, mediaTypeExclusive: Boolean) =
        SelectionCreator(this, mimeTypes, mediaTypeExclusive)
}


================================================
FILE: matisse/src/main/java/com/matisse/MimeType.kt
================================================
package com.matisse

/**
 * Describe : MIME Type enumeration to restrict selectable media on the selection activity.
 * Matisse only supports images and videos.
 * Created by Leo on 2018/8/29 on 14:55.
 */
enum class MimeType {

    // ============== images ==============

    JPG {
        override fun getValue() = MimeTypeManager.arraySetOf("jpg", "jpeg")
        override fun getKey() = "image/jpg"
    },

    JPEG {
        override fun getValue() = MimeTypeManager.arraySetOf("jpg", "jpeg")
        override fun getKey() = "image/jpeg"
    },

    PNG {
        override fun getValue() = MimeTypeManager.arraySetOf("png")
        override fun getKey() = "image/png"
    },

    GIF {
        override fun getValue() = MimeTypeManager.arraySetOf("gif")
        override fun getKey() = "image/gif"
    },

    BMP {
        override fun getValue() = MimeTypeManager.arraySetOf("bmp")
        override fun getKey() = "image/x-ms-bmp"
    },

    WEBP {
        override fun getValue() = MimeTypeManager.arraySetOf("webp")
        override fun getKey() = "image/webp"
    },

    // ============== videos ==============
    MPEG {
        override fun getValue() = MimeTypeManager.arraySetOf("mpg")
        override fun getKey() = "video/mpeg"
    },

    MP4 {
        override fun getValue() = MimeTypeManager.arraySetOf("m4v", "mp4")
        override fun getKey() = "video/mp4"
    },

    QUICKTIME {
        override fun getValue() = MimeTypeManager.arraySetOf("mov")
        override fun getKey() = "video/quicktime"
    },

    THREEGPP {
        override fun getValue() = MimeTypeManager.arraySetOf("3gp", "3gpp")
        override fun getKey() = "video/3gpp"
    },

    THREEGPP2 {
        override fun getValue() = MimeTypeManager.arraySetOf("3g2", "3gpp2")
        override fun getKey() = "video/3gpp2"
    },

    MKV {
        override fun getValue() = MimeTypeManager.arraySetOf("mkv")
        override fun getKey() = "video/x-matroska"
    },

    WEBM {
        override fun getValue() = MimeTypeManager.arraySetOf("webm")
        override fun getKey() = "video/webm"
    },

    TS {
        override fun getValue() = MimeTypeManager.arraySetOf("ts")
        override fun getKey() = "video/mp2ts"
    },

    AVI {
        override fun getValue() = MimeTypeManager.arraySetOf("avi")
        override fun getKey() = "video/avi"
    };

    abstract fun getValue(): Set<String>
    abstract fun getKey(): String
}

================================================
FILE: matisse/src/main/java/com/matisse/MimeTypeManager.kt
================================================
package com.matisse

import android.content.Context
import android.net.Uri
import android.text.TextUtils
import android.webkit.MimeTypeMap
import androidx.collection.ArraySet
import com.matisse.utils.PhotoMetadataUtils
import com.matisse.utils.getRealFilePath
import java.util.*

/**
 * Describe : Define MediaType
 * Created by Leo on 2018/8/29 on 15:02.
 */
class MimeTypeManager {

    companion object {
        fun ofAll(): EnumSet<MimeType> = EnumSet.allOf(MimeType::class.java)

        fun of(first: MimeType, others: Array<MimeType>): EnumSet<MimeType> =
            EnumSet.of(first, *others)

        fun ofImage(): EnumSet<MimeType> = EnumSet.of(
            MimeType.JPEG, MimeType.JPG, MimeType.PNG, MimeType.GIF, MimeType.BMP, MimeType.WEBP
        )

        // 静态图
        fun ofMotionlessImage(): EnumSet<MimeType> = EnumSet.of(
            MimeType.JPEG, MimeType.JPG, MimeType.PNG, MimeType.BMP
        )

        fun ofVideo(): EnumSet<MimeType> = EnumSet.of(
            MimeType.MPEG, MimeType.MP4, MimeType.QUICKTIME, MimeType.THREEGPP, MimeType.THREEGPP2,
            MimeType.MKV, MimeType.WEBM, MimeType.TS, MimeType.AVI
        )

        fun isImage(mimeType: String?) =
            isMotionlessImage(mimeType)
                    || MimeType.GIF.getKey().contains(lowerCaseMimeType(mimeType))
                    || MimeType.WEBP.getKey().contains(lowerCaseMimeType(mimeType))

        private fun isMotionlessImage(mimeType: String?) =
            MimeType.JPEG.getKey().contains(lowerCaseMimeType(mimeType))
                    || MimeType.JPG.getKey().contains(lowerCaseMimeType(mimeType))
                    || MimeType.PNG.getKey().contains(lowerCaseMimeType(mimeType))
                    || MimeType.BMP.getKey().contains(lowerCaseMimeType(mimeType))

        fun isVideo(mimeType: String) = MimeType.MPEG.getKey().contains(lowerCaseMimeType(mimeType))
                || MimeType.MP4.getKey().contains(lowerCaseMimeType(mimeType))
                || MimeType.QUICKTIME.getKey().contains(lowerCaseMimeType(mimeType))
                || MimeType.THREEGPP.getKey().contains(lowerCaseMimeType(mimeType))
                || MimeType.THREEGPP2.getKey().contains(lowerCaseMimeType(mimeType))
                || MimeType.MKV.getKey().contains(lowerCaseMimeType(mimeType))
                || MimeType.WEBM.getKey().contains(lowerCaseMimeType(mimeType))
                || MimeType.TS.getKey().contains(lowerCaseMimeType(mimeType))
                || MimeType.AVI.getKey().contains(lowerCaseMimeType(mimeType))

        fun isGif(mimeType: String) = MimeType.GIF.getKey().contains(lowerCaseMimeType(mimeType))

        fun arraySetOf(vararg suffixes: String) = ArraySet(mutableListOf(*suffixes))

        fun checkType(context: Context, uri: Uri?, mExtensions: Set<String>): Boolean {
            val map = MimeTypeMap.getSingleton()
            if (uri == null) return false

            val type = map.getExtensionFromMimeType(context.contentResolver.getType(uri))
            var path: String? = null
            // lazy load the path and prevent resolve for multiple times
            var pathParsed = false
            mExtensions.forEach {
                if (it == type) return true

                if (!pathParsed) {
                    // we only resolve the path for one time
                    path = getRealFilePath(context, uri)
                    if (!TextUtils.isEmpty(path)) path = path?.toLowerCase(Locale.US)
                    pathParsed = true
                }
                if (path != null && path?.endsWith(it) == true) return true
            }
            return false
        }

        private fun lowerCaseMimeType(mimeType: String?) = mimeType?.toLowerCase() ?: ""
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/SelectionCreator.kt
================================================
package com.matisse

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo.*
import android.os.Build
import android.view.View
import androidx.annotation.IntDef
import androidx.annotation.RequiresApi
import androidx.annotation.StyleRes
import com.matisse.engine.ImageEngine
import com.matisse.entity.CaptureStrategy
import com.matisse.filter.Filter
import com.matisse.internal.entity.SelectionSpec
import com.matisse.listener.OnCheckedListener
import com.matisse.listener.OnSelectedListener
import com.matisse.ui.activity.BaseActivity
import com.matisse.ui.activity.matisse.MatisseActivity
import java.io.File

/**
 * Fluent API for building media select specification.
 * Constructs a new specification builder on the context.
 *
 * @param matisse   a requester context wrapper.
 * @param mimeTypes MIME type set to select.
 */
class SelectionCreator(
    private val matisse: Matisse, mimeTypes: Set<MimeType>, mediaTypeExclusive: Boolean
) {
    private val selectionSpec: SelectionSpec = SelectionSpec.getCleanInstance()

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @IntDef(
        SCREEN_ORIENTATION_UNSPECIFIED, SCREEN_ORIENTATION_LANDSCAPE,
        SCREEN_ORIENTATION_PORTRAIT, SCREEN_ORIENTATION_USER, SCREEN_ORIENTATION_BEHIND,
        SCREEN_ORIENTATION_SENSOR, SCREEN_ORIENTATION_NOSENSOR, SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
        SCREEN_ORIENTATION_SENSOR_PORTRAIT, SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
        SCREEN_ORIENTATION_REVERSE_PORTRAIT, SCREEN_ORIENTATION_FULL_SENSOR,
        SCREEN_ORIENTATION_USER_LANDSCAPE, SCREEN_ORIENTATION_USER_PORTRAIT,
        SCREEN_ORIENTATION_FULL_USER, SCREEN_ORIENTATION_LOCKED
    )
    @kotlin.annotation.Retention(AnnotationRetention.SOURCE)
    internal annotation class ScreenOrientation

    init {
        selectionSpec.run {
            this.mimeTypeSet = mimeTypes
            this.mediaTypeExclusive = mediaTypeExclusive
            this.orientation = SCREEN_ORIENTATION_UNSPECIFIED
        }
    }

    /**
     * Theme for media selecting Activity.
     *
     * There are two built-in themes:
     * you can define a custom theme derived from the above ones or other themes.
     *
     * @param themeId theme resource id. Default value is R.style.Matisse_Zhihu.
     * @return [SelectionCreator] for fluent API.
     */
    fun theme(@StyleRes themeId: Int) = this.apply { selectionSpec.themeId = themeId }

    /**
     * Show a auto-increased number or a check mark when user select media.
     *
     * @param countable true for a auto-increased number from 1, false for a check mark. Default
     * value is false.
     * @return [SelectionCreator] for fluent API.
     */
    fun countable(countable: Boolean) = this.apply { selectionSpec.countable = countable }

    /**
     * Maximum selectable count.
     * mediaTypeExclusive true
     *      use maxSelectable
     * mediaTypeExclusive false
     *      use maxImageSelectable and maxVideoSelectable
     * @param maxSelectable Maximum selectable count. Default value is 1.
     * @return [SelectionCreator] for fluent API.
     */
    fun maxSelectable(maxSelectable: Int) = this.apply {
        if (!selectionSpec.mediaTypeExclusive) return this
        require(maxSelectable >= 1) { "maxSelectable must be greater than or equal to one" }
        check(!(selectionSpec.maxImageSelectable > 0 || selectionSpec.maxVideoSelectable > 0)) {
            "already set maxImageSelectable and maxVideoSelectable"
        }
        selectionSpec.maxSelectable = maxSelectable
    }

    /**
     * Only useful when [SelectionSpec.mediaTypeExclusive] set true and you want to set different maximum
     * selectable files for image and video media types.
     *
     * @param maxImageSelectable Maximum selectable count for image.
     * @param maxVideoSelectable Maximum selectable count for video.
     * @return
     */
    fun maxSelectablePerMediaType(maxImageSelectable: Int, maxVideoSelectable: Int) = this.apply {
        if (selectionSpec.mediaTypeExclusive) return this
        require(!(maxImageSelectable < 1 || maxVideoSelectable < 1)) {
            "mediaTypeExclusive must be false and max selectable must be greater than or equal to one"
        }
        selectionSpec.maxSelectable = -1
        selectionSpec.maxImageSelectable = maxImageSelectable
        selectionSpec.maxVideoSelectable = maxVideoSelectable
    }

    /**
     * Add filter to filter each selecting item.
     *
     * @param filter [Filter]
     * @return [SelectionCreator] for fluent API.
     */
    fun addFilter(filter: Filter) = apply {
        if (selectionSpec.filters == null) selectionSpec.filters = mutableListOf()
        selectionSpec.filters?.add(filter)
    }

    /**
     * Determines whether the photo capturing is enabled or not on the media grid view.
     * If this value is set true, photo capturing entry will appear only on All Media's page.
     *
     * @param enable Whether to enable capturing or not. Default value is false;
     * @return [SelectionCreator] for fluent API.
     */
    fun capture(enable: Boolean) = this.apply { selectionSpec.capture = enable }

    /**
     * Show a original photo check options.Let users decide whether use original photo after select
     *
     * @param enable Whether to enable original photo or not
     * @return [SelectionCreator] for fluent API.
     */
    fun originalEnable(enable: Boolean) = this.apply { selectionSpec.originalable = enable }

    /**
     * Maximum original size,the unit is MB. Only useful when {link@originalEnable} set true
     *
     * @param size Maximum original size. Default value is Integer.MAX_VALUE
     * @return [SelectionCreator] for fluent API.
     */
    fun maxOriginalSize(size: Int) = this.apply { selectionSpec.originalMaxSize = size }

    /**
     * Capture strategy provided for the location to save photos including internal and external
     * storage and also a authority for [androidx.core.content.FileProvider].
     *
     * @param captureStrategy [CaptureStrategy], needed only when capturing is enabled.
     * @return [SelectionCreator] for fluent API.
     */
    fun captureStrategy(captureStrategy: CaptureStrategy) = this.apply {
        selectionSpec.captureStrategy = captureStrategy
    }

    /**
     * Set the desired orientation of this activity.
     *
     * @param orientation An orientation constant as used in [ScreenOrientation].
     * Default value is [android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT].
     * @return [SelectionCreator] for fluent API.
     * @see Activity.setRequestedOrientation
     */
    fun restrictOrientation(@ScreenOrientation orientation: Int) = this.apply {
        selectionSpec.orientation = orientation
    }

    /**
     * Set a fixed span count for the media grid. Same for different screen orientations.
     * This will be ignored when [.gridExpectedSize] is set.
     * [get gridExpectedSize first]
     * @param spanCount Requested span count.
     * @return [SelectionCreator] for fluent API.
     */
    fun spanCount(spanCount: Int) = this.apply {
        if (selectionSpec.gridExpectedSize > 0) return this
        selectionSpec.spanCount = spanCount
    }

    /**
     * Set expected size for media grid to adapt to different screen sizes. This won't necessarily
     * be applied cause the media grid should fill the view container. The measured media grid's
     * size will be as close to this value as possible.
     *
     * @param sizePx Expected media grid size in pixel.
     * @return [SelectionCreator] for fluent API.
     */
    fun gridExpectedSize(sizePx: Int) = this.apply { selectionSpec.gridExpectedSize = sizePx }

    /**
     * Photo thumbnail's scale compared to the View's size. It should be a float value in (0.0,1.0].
     *
     * @param scale Thumbnail's scale in (0.0, 1.0]. Default value is 0.5.
     * @return [SelectionCreator] for fluent API.
     */
    fun thumbnailScale(scale: Float) = this.apply {
        require(!(scale <= 0f || scale > 1f)) { "Thumbnail scale must be between (0.0, 1.0]" }
        selectionSpec.thumbnailScale = scale
    }

    /**
     * Provide an image engine.
     * There are two built-in image engines:
     * And you can implement your own image engine.
     *
     * @param imageEngine [ImageEngine]
     * @return [SelectionCreator] for fluent API.
     */
    fun imageEngine(imageEngine: ImageEngine) = this.apply {
        selectionSpec.imageEngine = imageEngine
        selectionSpec.imageEngine?.init(matisse.activity?.applicationContext!!)
    }

    /**
     * Whether to support crop
     * If this value is set true, it will support function crop.
     * @param crop Whether to support crop or not. Default value is false;
     * @return [SelectionCreator] for fluent API.
     */
    fun isCrop(crop: Boolean) = this.apply { selectionSpec.isCrop = crop }

    /**
     * isCircleCrop
     * default is RECTANGLE CROP
     */
    fun isCircleCrop(isCircle: Boolean) = this.apply {
        selectionSpec.isCircleCrop = isCircle
    }

    /**
     * provide file to save image after crop
     */
    fun cropCacheFolder(cropCacheFolder: File) = this.apply {
        selectionSpec.cropCacheFolder = cropCacheFolder
    }

    /**
     * Set listener for callback immediately when user select or unselect something.
     *
     * It's a redundant API with [Matisse.obtainResult],
     * we only suggest you to use this API when you need to do something immediately.
     *
     * @param listener [OnSelectedListener]
     * @return [SelectionCreator] for fluent API.
     */
    fun setOnSelectedListener(listener: OnSelectedListener?) = this.apply {
        selectionSpec.onSelectedListener = listener
    }

    /**
     * Set listener for callback immediately when user check or uncheck original.
     *
     * @param listener [OnSelectedListener]
     * @return [SelectionCreator] for fluent API.
     */
    fun setOnCheckedListener(listener: OnCheckedListener?) = this.apply {
        selectionSpec.onCheckedListener = listener
    }

    /**
     * set notice type for matisse
     */
    fun setNoticeConsumer(
        noticeConsumer: ((context: Context, noticeType: Int, title: String, message: String) -> Unit)?
    ) = this.apply {
        selectionSpec.noticeEvent = noticeConsumer
    }

    /**
     * set Status Bar
     */
    fun setStatusBarFuture(statusBarFunction: ((params: BaseActivity, view: View?) -> Unit)?) =
        this.apply {
            selectionSpec.statusBarFuture = statusBarFunction
        }

    /**
     * set last choose pictures ids
     * id is cursor id. not support crop picture
     * 预选中上次带回的图片
     * 注:暂时无法保持预选中图片的顺序
     */
    fun setLastChoosePicturesIdOrUri(list: ArrayList<String>?) = this.apply {
        selectionSpec.lastChoosePictureIdsOrUris = list
    }

    /**
     * Start to select media and wait for result.
     *
     * @param requestCode Identity of the request Activity or Fragment.
     */
    fun forResult(requestCode: Int) {
        val activity = matisse.activity ?: return

        val intent = Intent(activity, MatisseActivity::class.java)

        val fragment = matisse.fragment
        if (fragment != null) {
            fragment.startActivityForResult(intent, requestCode)
        } else {
            activity.startActivityForResult(intent, requestCode)
        }
    }
}


================================================
FILE: matisse/src/main/java/com/matisse/engine/ImageEngine.kt
================================================
package com.matisse.engine

import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.widget.ImageView

/**
 * Describe : Image loader interface. There are predefined
 * Created by Leo on 2018/9/6 on 17:01.
 */
interface ImageEngine {

    /**
     * Load thumbnail of a static image resource
     *
     * @param context context
     * @param resize Desired size of the origin image
     * @param placeholder Placeholder drawable when image is not loaded yet
     * @param imageView ImageView widget
     * @param uri Uri of the loaded image
     */
    fun loadThumbnail(
        context: Context, resize: Int, placeholder: Drawable?,
        imageView: ImageView, uri: Uri?
    )

    /**
     * Load thumbnail of a static image resource
     *
     * @param context context
     * @param resize Desired size of the origin image
     * @param placeholder Placeholder drawable when image is not loaded yet
     * @param imageView ImageView widget
     * @param uri Uri of the loaded image
     */
    fun loadGifThumbnail(
        context: Context, resize: Int, placeholder: Drawable?,
        imageView: ImageView, uri: Uri?
    )

    /**
     * Load a gif image resource
     *
     * @param context context
     * @param imageView ImageView widget
     * @param uri Uri of the loaded image
     */
    fun loadImage(context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri?)

    /**
     * Load a gif image resource
     *
     * @param context context
     * @param resizeX Desired x-size of the origin image
     * @param resizeY Desired y-size of the origin image
     * @param imageView ImageView widget
     * @param uri Uri of the loaded image
     */
    fun loadGifImage(context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri?)

    fun cleanMemory(context: Context)

    fun pause(context: Context)

    fun resume(context: Context)

    // 在application的onCreate中初始化
    fun init(context: Context)
}

================================================
FILE: matisse/src/main/java/com/matisse/entity/Album.kt
================================================
package com.matisse.entity

import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Parcel
import android.os.Parcelable
import com.matisse.R
import com.matisse.loader.AlbumLoader

class Album() : Parcelable {
    private var id = ""
    private var coverUri: Uri? = null
    private var displayName = ""
    private var count: Long = 0
    private var isCheck = false

    constructor(parcel: Parcel) : this() {
        id = parcel.readString() ?: ""
        coverUri = parcel.readParcelable(Uri::class.java.classLoader)
        displayName = parcel.readString() ?: ""
        count = parcel.readLong()
        isCheck = parcel.readByte() != 0.toByte()
    }

    constructor(mCoverUri: Uri?, mDisplayName: String, mCount: Long) :
            this("-1", mCoverUri, mDisplayName, mCount)

    constructor(mDisplayName: String, mCount: Long) :
            this(System.currentTimeMillis().toString(), mDisplayName, mCount, false)

    constructor(mId: String, mCoverUri: Uri?, mDisplayName: String, mCount: Long) :
            this() {
        this.id = mId
        this.coverUri = mCoverUri
        this.displayName = mDisplayName
        this.count = mCount
        this.isCheck = false
    }

    constructor(
        mId: String, mDisplayName: String, mCount: Long, mIsCheck: Boolean = false
    ) : this() {
        this.id = mId
        this.displayName = mDisplayName
        this.count = mCount
        this.isCheck = mIsCheck
    }

    fun getId() = id

    fun getCoverPath() = coverUri

    fun setCoverPath(path: Uri?) {
        path?.apply { coverUri = this }
    }

    fun getCount() = count

    fun addCaptureCount() {
        count++
    }

    fun getDisplayName(context: Context): String {
        return if (isAll()) {
            context.getString(R.string.album_name_all)
        } else displayName
    }

    fun isAll() = ALBUM_ID_ALL == id

    fun isEmpty() = count == 0L

    fun isChecked() = isCheck

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(id)
        parcel.writeParcelable(coverUri, 0)
        parcel.writeString(displayName)
        parcel.writeLong(count)
        parcel.writeByte(if (isCheck) 1 else 0)
    }

    override fun describeContents() = 0

    companion object CREATOR : Parcelable.Creator<Album> {

        const val ALBUM_ID_ALL = (-1).toString()
        const val ALBUM_NAME_ALL = "All"

        override fun createFromParcel(parcel: Parcel): Album {
            return Album(parcel)
        }

        override fun newArray(size: Int): Array<Album?> {
            return arrayOfNulls(size)
        }

        fun valueOf(cursor: Cursor) = Album(
            cursor.getString(cursor.getColumnIndex(AlbumLoader.BUCKET_ID)),
            Uri.parse(cursor.getString(cursor.getColumnIndex(AlbumLoader.COLUMN_URI)) ?: ""),
            cursor.getString(cursor.getColumnIndex(AlbumLoader.BUCKET_DISPLAY_NAME)),
            cursor.getLong(cursor.getColumnIndex(AlbumLoader.COLUMN_COUNT))
        )
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/entity/CaptureStrategy.kt
================================================
package com.matisse.entity

data class CaptureStrategy(var isPublic: Boolean, var authority: String, var directory: String = "")

================================================
FILE: matisse/src/main/java/com/matisse/entity/ConstValue.kt
================================================
package com.matisse.entity

import com.matisse.ucrop.UCrop

object ConstValue {
    const val EXTRA_RESULT_SELECTION = "extra_result_selection"
    const val EXTRA_RESULT_SELECTION_ID = "extra_result_selection_id"
    const val EXTRA_RESULT_ORIGINAL_ENABLE = "extra_result_original_enable"
    const val EXTRA_ALBUM = "extra_album"
    const val EXTRA_ITEM = "extra_item"

    const val CHECK_STATE = "checkState"

    const val FOLDER_CHECK_POSITION = "folder_check_position"

    const val EXTRA_DEFAULT_BUNDLE = "extra_default_bundle"
    const val EXTRA_RESULT_BUNDLE = "extra_result_bundle"
    const val EXTRA_RESULT_CROP_BACK_BUNDLE = UCrop.EXTRA_OUTPUT_URI
    const val EXTRA_RESULT_APPLY = "extra_result_apply"

    const val STATE_SELECTION = "state_selection"
    const val STATE_COLLECTION_TYPE = "state_collection_type"

    const val REQUEST_CODE_PREVIEW = 23
    const val REQUEST_CODE_CAPTURE = 24
    const val REQUEST_CODE_CROP = 69            // 对应UCrop中的key
    const val REQUEST_CODE_CROP_ERROR = 96      // 对应UCrop中的key
    const val REQUEST_CODE_CHOOSE = 26
}

================================================
FILE: matisse/src/main/java/com/matisse/entity/IncapableCause.kt
================================================
package com.matisse.entity

import android.content.Context
import android.widget.Toast
import androidx.annotation.IntDef
import androidx.fragment.app.FragmentActivity
import com.matisse.internal.entity.SelectionSpec
import com.matisse.widget.IncapableDialog

class IncapableCause {

    companion object {
        const val TOAST = 0x0001
        const val DIALOG = 0x0002
        const val LOADING = 0x0003
        const val NONE = 0x0004

        fun handleCause(context: Context, cause: IncapableCause?) {
            if (cause?.noticeEvent != null) {
                cause.noticeEvent?.invoke(
                    context, cause.form, cause.title ?: "", cause.message ?: ""
                )
                return
            }

            when (cause?.form) {
                DIALOG -> {
                    IncapableDialog.newInstance(cause.title, cause.message)
                        .show(
                            (context as FragmentActivity).supportFragmentManager,
                            IncapableDialog::class.java.name
                        )
                }

                TOAST -> {
                    Toast.makeText(context, cause.message, Toast.LENGTH_SHORT).show()
                }

                LOADING -> {
                    // TODO Leo 2019-12-24 complete loading
                }
            }
        }
    }

    @Retention(AnnotationRetention.SOURCE)
    @IntDef(TOAST, DIALOG, LOADING, NONE)
    annotation class Form

    var form = TOAST
    var title: String? = null
    var message: String? = null
    var dismissLoading: Boolean? = null
    var noticeEvent: ((
        context: Context, noticeType: Int, title: String, msg: String
    ) -> Unit)? = null

    constructor(message: String) : this(TOAST, message)
    constructor(@Form form: Int, message: String) : this(form, "", message)
    constructor(@Form form: Int, title: String, message: String) : this(form, title, message, true)
    constructor(@Form form: Int, title: String, message: String, dismissLoading: Boolean) {
        this.form = form
        this.title = title
        this.message = message
        this.dismissLoading = dismissLoading

        this.noticeEvent = SelectionSpec.getInstance().noticeEvent
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/entity/Item.kt
================================================
package com.matisse.entity

import android.content.ContentUris
import android.database.Cursor
import android.net.Uri
import android.os.Parcelable
import android.provider.MediaStore
import com.matisse.MimeTypeManager
import kotlinx.android.parcel.IgnoredOnParcel
import kotlinx.android.parcel.Parcelize

@Parcelize
class Item(
    var id: Long, private var mimeType: String, var size: Long = 0,
    var duration: Long = 0, var positionInList: Int = -1
) : Parcelable {

    companion object {
        const val ITEM_ID_CAPTURE: Long = -1
        const val ITEM_DISPLAY_NAME_CAPTURE = "Capture"

        // * 注:资源文件size单位为字节byte
        fun valueOf(cursor: Cursor?, positionInList: Int = -1) = cursor?.let {
            Item(
                it.getLong(it.getColumnIndex(MediaStore.Files.FileColumns._ID)),
                it.getString(it.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)),
                it.getLong(it.getColumnIndex(MediaStore.MediaColumns.SIZE)),
                it.getLong(it.getColumnIndex("duration")), positionInList
            )
        }
    }

    @IgnoredOnParcel
    private var uri: Uri

    init {
        val contentUri = when {
            isImage() -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            isVideo() -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            else -> MediaStore.Files.getContentUri("external")
        }
        uri = ContentUris.withAppendedId(contentUri, id)
    }

    fun isImage() = MimeTypeManager.isImage(mimeType)

    fun isGif() = MimeTypeManager.isGif(mimeType)

    fun isVideo() = MimeTypeManager.isVideo(mimeType)

    fun getContentUri() = uri

    fun isCapture() = id == ITEM_ID_CAPTURE

    override fun describeContents() = 0

    override fun equals(other: Any?): Boolean {
        if (other !is Item) return false

        val otherItem = other as Item?
        return ((id == otherItem?.id && (mimeType == otherItem.mimeType))
                && (uri == otherItem.uri) && size == otherItem.size && duration == otherItem.duration)
    }

    override fun hashCode(): Int {
        var result = 1
        result = 31 * result + mimeType.hashCode()
        result = 31 * result + uri.hashCode()
        result = 31 * result + size.toString().hashCode()
        result = 31 * result + duration.toString().hashCode()
        return result
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/filter/Filter.kt
================================================
package com.matisse.filter

import android.content.Context
import com.matisse.MimeType
import com.matisse.MimeTypeManager
import com.matisse.entity.IncapableCause
import com.matisse.entity.Item

/**
 * Describe : Filter for choosing a {@link Item}. You can add multiple Filters through
 * {@link SelectionCreator #addFilter(Filter)}.
 * Created by Leo on 2018/9/4 on 16:12.
 */
abstract class Filter {
    companion object {

        // Convenient constant for a minimum value
        const val MIN = 0

        // Convenient constant for a maximum value
        const val MAX = Int.MAX_VALUE

        // Convenient constant for 1024
        const val K = 1024
    }

    // Against what mime types this filter applies
    abstract fun constraintTypes(): Set<MimeType>

    /**
     * Invoked for filtering each item
     *
     * @return null if selectable, {@link IncapableCause} if not selectable.
     */
    abstract fun filter(context: Context, item: Item?): IncapableCause?

    // Whether an {@link Item} need filtering
    open fun needFiltering(context: Context, item: Item?): Boolean {
        constraintTypes().forEach {
            if (MimeTypeManager.checkType(context, item?.getContentUri(), it.getValue())
            ) return true
        }

        return false
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/internal/entity/SelectionSpec.kt
================================================
package com.matisse.internal.entity

import android.content.Context
import android.content.pm.ActivityInfo
import android.view.View
import androidx.annotation.StyleRes
import com.matisse.MimeType
import com.matisse.MimeTypeManager
import com.matisse.R
import com.matisse.engine.ImageEngine
import com.matisse.entity.CaptureStrategy
import com.matisse.entity.Item
import com.matisse.filter.Filter
import com.matisse.listener.OnCheckedListener
import com.matisse.listener.OnSelectedListener
import com.matisse.ui.activity.BaseActivity
import java.io.File

/**
 * Describe : Builder to get config values
 * Created by Leo on 2018/8/29 on 14:54.
 */
class SelectionSpec {
    var mimeTypeSet: Set<MimeType>? = null
    var mediaTypeExclusive = false                      // 设置单种/多种媒体资源选择 默认支持多种
    var filters: MutableList<Filter>? = null
    var maxSelectable = 1
    var maxImageSelectable = 0
    var maxVideoSelectable = 0
    var thumbnailScale = 0.5f
    var countable = false
    var capture = false
    var gridExpectedSize = 0
    var spanCount = 3
    var captureStrategy: CaptureStrategy? = null
    @StyleRes
    var themeId = R.style.Matisse_Default
    var orientation = 0
    var originalable = false
    var originalMaxSize = 0
    var imageEngine: ImageEngine? = null
    var onSelectedListener: OnSelectedListener? = null
    var onCheckedListener: OnCheckedListener? = null

    var isCrop = false                              // 裁剪
    var isCircleCrop = false                        // 裁剪框的形状
    var cropCacheFolder: File? = null               // 裁剪后文件保存路径

    var hasInited = false                           // 是否初始化完成

    // 库内提示具体回调
    var noticeEvent: ((
        context: Context, noticeType: Int, title: String, msg: String
    ) -> Unit)? = null

    // 状态栏处理回调
    var statusBarFuture: ((params: BaseActivity, view: View?) -> Unit)? = null

    var lastChoosePictureIdsOrUris: ArrayList<String>? = null   // 上次选中的图片Id

    class InstanceHolder {
        companion object {
            val INSTANCE: SelectionSpec = SelectionSpec()
        }
    }

    companion object {

        fun getInstance() = InstanceHolder.INSTANCE

        fun getCleanInstance(): SelectionSpec {
            val selectionSpec = getInstance()
            selectionSpec.reset()
            return selectionSpec
        }
    }

    private fun reset() {
        mimeTypeSet = null
        mediaTypeExclusive = false
        themeId = R.style.Matisse_Default
        orientation = 0
        countable = false
        maxSelectable = 1
        maxImageSelectable = 0
        maxVideoSelectable = 0
        filters = null
        capture = false
        captureStrategy = null
        spanCount = 3
        gridExpectedSize = 0
        thumbnailScale = 0.5f

        imageEngine = null
        hasInited = true

        // crop
        isCrop = false
        isCircleCrop = false

        // return original setting
        originalable = false
        originalMaxSize = Integer.MAX_VALUE

        noticeEvent = null
        statusBarFuture = null

        lastChoosePictureIdsOrUris = null
    }

    // 是否可计数
    fun isCountable() = countable && !isSingleChoose()

    // 是否可单选
    fun isSingleChoose() =
        maxSelectable == 1 || (maxImageSelectable == 1 && maxVideoSelectable == 1)

    // 是否可裁剪
    fun openCrop() = isCrop && isSingleChoose()

    fun isSupportCrop(item: Item?) = item != null && item.isImage() && !item.isGif()

    // 是否单一资源选择方式
    fun isMediaTypeExclusive() =
        mediaTypeExclusive && (maxImageSelectable + maxVideoSelectable == 0)

    fun onlyShowImages() =
        if (mimeTypeSet != null) MimeTypeManager.ofImage().containsAll(mimeTypeSet!!) else false

    fun onlyShowVideos() =
        if (mimeTypeSet != null) MimeTypeManager.ofVideo().containsAll(mimeTypeSet!!) else false

    fun singleSelectionModeEnabled() = !countable && isSingleChoose()

    fun needOrientationRestriction() = orientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}

================================================
FILE: matisse/src/main/java/com/matisse/listener/OnCheckedListener.kt
================================================
package com.matisse.listener

/**
 * Created by Lijianyou on 2018-09-07.
 * @author  Lijianyou
 */
interface OnCheckedListener {
    fun onCheck(isChecked: Boolean)
}

================================================
FILE: matisse/src/main/java/com/matisse/listener/OnSelectedListener.kt
================================================
package com.matisse.listener

import android.net.Uri

/**
 * Created by Lijianyou on 2018-09-07.
 * @author  Lijianyou
 */
interface OnSelectedListener {
    /**
     * @param uriList the selected item [Uri] list.
     * @param pathList the selected item file path list.
     */
    fun onSelected(uriList: List<Uri>, pathList: List<String>)
}

================================================
FILE: matisse/src/main/java/com/matisse/loader/AlbumLoader.kt
================================================
package com.matisse.loader

import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.database.MatrixCursor
import android.database.MergeCursor
import android.net.Uri
import android.provider.MediaStore
import androidx.loader.content.CursorLoader
import com.matisse.MimeTypeManager
import com.matisse.entity.Album
import com.matisse.internal.entity.SelectionSpec
import com.matisse.utils.Platform.beforeAndroidTen
import java.util.*

/**
 * Describe : Load all albums(group by bucket_id) into a single cursor
 * Created by Leo on 2018/8/29 on 14:28.
 */
class AlbumLoader(context: Context, selection: String, selectionArgs: Array<out String>) :
    CursorLoader(
        context, QUERY_URI, if (beforeAndroidTen()) PROJECTION else PROJECTION_29,
        selection, selectionArgs, BUCKET_ORDER_BY
    ) {

    companion object {
        const val COLUMN_COUNT = "count"
        private val QUERY_URI = MediaStore.Files.getContentUri("external")
        const val BUCKET_ID = "bucket_id"
        const val BUCKET_DISPLAY_NAME = "bucket_display_name"
        private const val BUCKET_ORDER_BY = "datetaken DESC"

        const val COLUMN_URI = "uri"

        val COLUMNS = arrayOf(
            MediaStore.Files.FileColumns._ID, BUCKET_ID, BUCKET_DISPLAY_NAME,
            MediaStore.MediaColumns.MIME_TYPE, COLUMN_URI, COLUMN_COUNT
        )

        val PROJECTION = arrayOf(
            MediaStore.Files.FileColumns._ID, BUCKET_ID, BUCKET_DISPLAY_NAME,
            MediaStore.MediaColumns.MIME_TYPE, "COUNT(*) AS $COLUMN_COUNT"
        )

        private val PROJECTION_29 = arrayOf(
            MediaStore.Files.FileColumns._ID, BUCKET_ID,
            BUCKET_DISPLAY_NAME, MediaStore.MediaColumns.MIME_TYPE
        )

        private const val SELECTION = "(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=? " +
                "OR " + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?) " +
                "AND " + MediaStore.MediaColumns.SIZE + ">0) GROUP BY (" + BUCKET_ID

        private const val SELECTION_29 = (
                "(" + MediaStore.Files.FileColumns.MEDIA_TYPE
                        + "=? OR " + MediaStore.Files.FileColumns.MEDIA_TYPE
                        + "=?) AND " + MediaStore.MediaColumns.SIZE + ">0")

        private val SELECTION_ARGS = arrayOf(
            MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
            MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString()
        )

        private const val SELECTION_FOR_SINGLE_MEDIA_TYPE =
            MediaStore.Files.FileColumns.MEDIA_TYPE + "=? AND " +
                    MediaStore.MediaColumns.SIZE + ">0) GROUP BY (" + BUCKET_ID

        private const val SELECTION_FOR_SINGLE_MEDIA_TYPE_29 = (
                MediaStore.Files.FileColumns.MEDIA_TYPE
                        + "=? AND " + MediaStore.MediaColumns.SIZE + ">0")

        private fun getSelectionArgsForSingleMediaType(mediaType: Int) =
            arrayOf(mediaType.toString())

        fun newInstance(context: Context): CursorLoader {
            var selection = if (beforeAndroidTen())
                SELECTION_FOR_SINGLE_MEDIA_TYPE
            else
                SELECTION_FOR_SINGLE_MEDIA_TYPE_29
            val selectionArgs: Array<String>

            when {
                SelectionSpec.getInstance().onlyShowImages() -> selectionArgs =
                    getSelectionArgsForSingleMediaType(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE)
                SelectionSpec.getInstance().onlyShowVideos() -> selectionArgs =
                    getSelectionArgsForSingleMediaType(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO)
                else -> {
                    selection = if (beforeAndroidTen()) SELECTION else SELECTION_29
                    selectionArgs = SELECTION_ARGS
                }
            }

            return AlbumLoader(context, selection, selectionArgs)
        }
    }

    override fun loadInBackground(): Cursor? {
        val albums = super.loadInBackground()
        val allAlbum = MatrixCursor(COLUMNS)
        return if (beforeAndroidTen()) loadBelowAndroidQ(albums, allAlbum)
        else loadAboveAndroidQ(albums, allAlbum)
    }

    private fun loadBelowAndroidQ(albums: Cursor?, allAlbum: MatrixCursor): MergeCursor {
        var totalCount = 0
        var allAlbumCoverUri: Uri? = null
        val otherAlbums = MatrixCursor(COLUMNS)
        albums?.apply {
            while (moveToNext()) {
                val fileId = getLong(getColumnIndex(MediaStore.Files.FileColumns._ID))
                val bucketId = getLong(getColumnIndex(BUCKET_ID))
                val bucketDisplayName = getString(getColumnIndex(BUCKET_DISPLAY_NAME))
                val mimeType = getString(getColumnIndex(MediaStore.MediaColumns.MIME_TYPE))
                val uri = getUri(albums)
                val count = getInt(getColumnIndex(COLUMN_COUNT))

                otherAlbums.addRow(
                    arrayOf(
                        fileId, bucketId, bucketDisplayName,
                        mimeType, uri.toString(), count.toString()
                    )
                )
                totalCount += count
            }
            if (albums.moveToFirst()) allAlbumCoverUri = getUri(albums)

        }

        allAlbumAddRow(allAlbumCoverUri, totalCount, allAlbum)
        return MergeCursor(arrayOf<Cursor>(allAlbum, otherAlbums))
    }

    private fun loadAboveAndroidQ(albums: Cursor?, allAlbum: MatrixCursor): MergeCursor {
        var totalCount = 0
        var allAlbumCoverUri: Uri? = null
        val otherAlbums = MatrixCursor(COLUMNS)

        // Pseudo GROUP BY
        val countMap = hashMapOf<Long, Long>()
        albums?.apply {
            while (moveToNext()) {
                val bucketId = getLong(getColumnIndex(BUCKET_ID))

                var count: Long? = countMap[bucketId]
                if (count == null) count = 1L else count++

                countMap[bucketId] = count
            }

            if (moveToFirst()) {
                allAlbumCoverUri = getUri(this)

                val done = HashSet<Long>()

                do {
                    val bucketId = getLong(getColumnIndex(BUCKET_ID))

                    if (done.contains(bucketId)) continue

                    val fileId = getLong(getColumnIndex(MediaStore.Files.FileColumns._ID))
                    val bucketDisplayName = getString(getColumnIndex(BUCKET_DISPLAY_NAME))
                    val mimeType = getString(getColumnIndex(MediaStore.MediaColumns.MIME_TYPE))
                    val uri = getUri(this)
                    val count = countMap[bucketId]

                    otherAlbums.addRow(
                        arrayOf<String>(
                            fileId.toString(), bucketId.toString(), bucketDisplayName,
                            mimeType, uri.toString(), count.toString()
                        )
                    )
                    done.add(bucketId)

                    totalCount += count?.toInt() ?: 0
                } while (albums.moveToNext())
            }
        }

        allAlbumAddRow(allAlbumCoverUri, totalCount, allAlbum)
        return MergeCursor(arrayOf<Cursor>(allAlbum, otherAlbums))
    }

    private fun allAlbumAddRow(allAlbumCoverUri: Uri?, totalCount: Int, allAlbum: MatrixCursor) {
        val row: Array<String?> = arrayOf(
            Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL,
            null, allAlbumCoverUri?.toString(), totalCount.toString()
        )
        allAlbum.addRow(row)
    }

    private fun getUri(cursor: Cursor): Uri {
        val id = cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID))
        val mimeType =
            cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)) ?: ""
        val contentUri = when {
            MimeTypeManager.isImage(mimeType) -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            MimeTypeManager.isVideo(mimeType) -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            else -> MediaStore.Files.getContentUri("external")
        }

        return ContentUris.withAppendedId(contentUri, id)
    }

    override fun onContentChanged() {
        // FIXME a dirty way to fix loading multiple times
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/loader/AlbumMediaLoader.kt
================================================
package com.matisse.loader

import android.content.Context
import android.database.Cursor
import android.database.MatrixCursor
import android.database.MergeCursor
import android.provider.MediaStore
import androidx.loader.content.CursorLoader
import com.matisse.entity.Album
import com.matisse.entity.Item
import com.matisse.internal.entity.SelectionSpec
import com.matisse.utils.MediaStoreCompat

/**
 * Load images and videos into a single cursor.
 * Created by Leo on 2018/9/4 on 19:53.
 */
class AlbumMediaLoader(
    context: Context, selection: String, selectionArgs: Array<out String>, capture: Boolean
) : CursorLoader(context, QUERY_URI, PROJECTION, selection, selectionArgs, ORDER_BY) {

    private var enableCapture = false

    init {
        enableCapture = capture
    }

    companion object {
        private val QUERY_URI = MediaStore.Files.getContentUri("external")

        val PROJECTION = arrayOf(
            MediaStore.Files.FileColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME,
            MediaStore.MediaColumns.MIME_TYPE, MediaStore.MediaColumns.SIZE, "duration"
        )

        private const val SELECTION_ALL = ("(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=? OR "
                + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?) AND " + MediaStore.MediaColumns.SIZE + ">0")

        private val SELECTION_ALL_ARGS = arrayOf(
            MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
            MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString()
        )
        // ===========================================================

        // === params for album ALL && showSingleMediaType: true ===
        private const val SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE = (
                MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
                        + " AND " + MediaStore.MediaColumns.SIZE + ">0")

        // === params for ordinary album && showSingleMediaType: false ===
        private const val SELECTION_ALBUM = (
                "(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
                        + " OR "
                        + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
                        + " AND "
                        + " bucket_id=?"
                        + " AND " + MediaStore.MediaColumns.SIZE + ">0")

        private fun getSelectionAlbumArgs(albumId: String): Array<String> {
            return arrayOf(
                MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
                MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString(), albumId
            )
        }

        // ===============================================================

        // === params for ordinary album && showSingleMediaType: true ===
        private const val SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE = (
                MediaStore.Files.FileColumns.MEDIA_TYPE
                        + "=? AND bucket_id=? AND " + MediaStore.MediaColumns.SIZE + ">0")

        // ===============================================================

        private const val ORDER_BY = MediaStore.Images.Media.DATE_TAKEN + " DESC"

        fun newInstance(context: Context, album: Album, capture: Boolean): CursorLoader {
            val selection: String
            val selectionArgs: Array<String>
            val enableCapture: Boolean

            if (album.isAll()) {
                when {
                    SelectionSpec.getInstance().onlyShowImages() -> {
                        selection = SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE
                        selectionArgs =
                            arrayOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString())
                    }
                    SelectionSpec.getInstance().onlyShowVideos() -> {
                        selection = SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE
                        selectionArgs =
                            arrayOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString())
                    }
                    else -> {
                        selection = SELECTION_ALL
                        selectionArgs = SELECTION_ALL_ARGS
                    }
                }
                enableCapture = capture
            } else {
                when {
                    SelectionSpec.getInstance().onlyShowImages() -> {
                        selection = SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE
                        selectionArgs = arrayOf(
                            MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(), album.getId()
                        )
                    }
                    SelectionSpec.getInstance().onlyShowVideos() -> {
                        selection = SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE
                        selectionArgs = arrayOf(
                            MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString(), album.getId()
                        )
                    }
                    else -> {
                        selection = SELECTION_ALBUM
                        selectionArgs = getSelectionAlbumArgs(album.getId())
                    }
                }
                enableCapture = false
            }
            return AlbumMediaLoader(context, selection, selectionArgs, enableCapture)
        }
    }

    override fun loadInBackground(): Cursor? {
        val result = super.loadInBackground()
        if (!enableCapture || !MediaStoreCompat.hasCameraFeature(context)) {
            return result
        }
        val dummy = MatrixCursor(PROJECTION)
        dummy.addRow(arrayOf(Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE, "", 0, 0))
        return MergeCursor(arrayOf(dummy, result!!))
    }

    override fun onContentChanged() {
        // FIXME a dirty way to fix loading multiple times
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/model/AlbumCallbacks.kt
================================================
package com.matisse.model

import android.database.Cursor

interface AlbumCallbacks {
    fun onAlbumStart()
    fun onAlbumLoad(cursor: Cursor)
    fun onAlbumReset()
}

================================================
FILE: matisse/src/main/java/com/matisse/model/AlbumCollection.kt
================================================
package com.matisse.model

import android.content.Context
import android.database.Cursor
import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader
import com.matisse.loader.AlbumLoader
import java.lang.ref.WeakReference

class AlbumCollection : LoaderManager.LoaderCallbacks<Cursor> {
    companion object {
        const val LOADER_ID = 1
        const val STATE_CURRENT_SELECTION = "state_current_selection"
    }

    private var context: WeakReference<Context>? = null
    private var loaderManager: LoaderManager? = null
    private var callbacks: AlbumCallbacks? = null
    private var currentSelection = 0
    private var loadFinished = false


    override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
        val context = context?.get()
        loadFinished = false
        return AlbumLoader.newInstance(context!!)
    }

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) {
        if (context?.get() == null || data == null) return

        if (!loadFinished) {
            loadFinished = true
            callbacks?.onAlbumLoad(data)
        }
    }

    override fun onLoaderReset(loader: Loader<Cursor>) {
        if (context?.get() == null) return

        callbacks?.onAlbumReset()
    }

    fun onCreate(activity: FragmentActivity, callbacks: AlbumCallbacks) {
        context = WeakReference(activity)
        loaderManager = LoaderManager.getInstance(activity)
        this.callbacks = callbacks
    }

    fun onRestoreInstanceState(saveInstanceState: Bundle) {
        currentSelection = saveInstanceState.getInt(STATE_CURRENT_SELECTION)
    }

    fun onSaveInstanceState(outState: Bundle?) {
        outState?.putInt(STATE_CURRENT_SELECTION, currentSelection)
    }

    fun onDestroy() {
        loaderManager?.destroyLoader(LOADER_ID)
        if (callbacks != null) callbacks = null
    }

    @Synchronized
    fun loadAlbums() {
        loadFinished = false
        loaderManager?.initLoader(LOADER_ID, null, this)
    }

    fun getCurrentSelection() = currentSelection

    fun setStateCurrentSelection(currentSelection: Int) {
        this.currentSelection = currentSelection
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/model/AlbumMediaCollection.kt
================================================
package com.matisse.model

import android.content.Context
import android.database.Cursor
import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader
import com.matisse.entity.Album
import com.matisse.loader.AlbumMediaLoader
import java.lang.ref.WeakReference

class AlbumMediaCollection : LoaderManager.LoaderCallbacks<Cursor> {

    companion object {
        const val LOADER_ID = 2
        const val ARGS_ALBUM = "args_album"
        const val ARGS_ENABLE_CAPTURE = "args_enable_capture"
    }

    private var context: WeakReference<Context>? = null
    private var loaderManager: LoaderManager? = null
    private var callbacks: AlbumCallbacks? = null


    fun onCreate(context: FragmentActivity, callbacks: AlbumCallbacks) {
        this.context = WeakReference(context)
        loaderManager = LoaderManager.getInstance(context)
        this.callbacks = callbacks
    }

    fun onDestroy() {
        loaderManager?.destroyLoader(LOADER_ID)
        if (callbacks != null) callbacks = null
    }

    fun load(target: Album) {
        load(target, false)
    }

    fun load(target: Album, enableCapture: Boolean) {
        val args = Bundle()
        args.putParcelable(ARGS_ALBUM, target)
        args.putBoolean(ARGS_ENABLE_CAPTURE, enableCapture)
        loaderManager?.initLoader(LOADER_ID, args, this)
    }

    override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
        val content = context?.get()

        val album = args?.getParcelable<Album>(ARGS_ALBUM)
        return AlbumMediaLoader.newInstance(
            content!!, album!!, album.isAll()
                    && args.getBoolean(ARGS_ENABLE_CAPTURE, false)
        )
    }

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) {
        if (context?.get() == null) return

        callbacks?.onAlbumLoad(data!!)
    }

    override fun onLoaderReset(loader: Loader<Cursor>) {
        if (context?.get() == null) return

        callbacks?.onAlbumReset()
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/model/SelectedItemCollection.kt
================================================
package com.matisse.model

import android.content.Context
import android.content.res.Resources
import android.net.Uri
import android.os.Bundle
import com.matisse.R
import com.matisse.entity.ConstValue.STATE_COLLECTION_TYPE
import com.matisse.entity.ConstValue.STATE_SELECTION
import com.matisse.entity.IncapableCause
import com.matisse.entity.Item
import com.matisse.internal.entity.SelectionSpec
import com.matisse.utils.PhotoMetadataUtils
import com.matisse.utils.getPath
import com.matisse.widget.CheckView
import java.util.*
import kotlin.collections.ArrayList

class SelectedItemCollection(private var context: Context) {

    companion object {
        /**
         * Empty collection
         */
        const val COLLECTION_UNDEFINED = 0x00
        /**
         * Collection only with images
         */
        const val COLLECTION_IMAGE = 0x01
        /**
         * Collection only with videos
         */
        const val COLLECTION_VIDEO = 0x02
        /**
         * Collection with images and videos.
         */
        const val COLLECTION_MIXED = COLLECTION_IMAGE or COLLECTION_VIDEO
    }

    private lateinit var items: LinkedHashSet<Item>
    private var imageItems: LinkedHashSet<Item>? = null
    private var videoItems: LinkedHashSet<Item>? = null
    private var collectionType = COLLECTION_UNDEFINED
    private val spec: SelectionSpec = SelectionSpec.getInstance()


    fun onCreate(bundle: Bundle?) {
        if (bundle == null) {
            items = linkedSetOf()
        } else {
            val saved = bundle.getParcelableArrayList<Item>(STATE_SELECTION)
            items = LinkedHashSet(saved!!)
            initImageOrVideoItems()
            collectionType = bundle.getInt(STATE_COLLECTION_TYPE, COLLECTION_UNDEFINED)
        }
    }

    /**
     * 根据混合选择模式,初始化图片与视频集合
     */
    private fun initImageOrVideoItems() {
        if (spec.isMediaTypeExclusive()) return
        items.forEach {
            addImageOrVideoItem(it)
        }
    }

    fun onSaveInstanceState(outState: Bundle?) {
        outState?.putParcelableArrayList(STATE_SELECTION, ArrayList(items))
        outState?.putInt(STATE_COLLECTION_TYPE, collectionType)
    }

    fun getDataWithBundle() = Bundle().run {
        putParcelableArrayList(STATE_SELECTION, ArrayList(items))
        putInt(STATE_COLLECTION_TYPE, collectionType)
        this
    }

    fun setDefaultSelection(uris: List<Item>) {
        items.addAll(uris)
    }

    private fun resetType() {
        if (items.size == 0) {
            collectionType = COLLECTION_UNDEFINED
        } else {
            if (collectionType == COLLECTION_MIXED) refineCollectionType()
        }
    }

    fun overwrite(items: ArrayList<Item>, collectionType: Int) {
        this.collectionType = if (items.size == 0) COLLECTION_UNDEFINED else collectionType

        this.items.clear()
        this.items.addAll(items)
    }

    fun asList() = ArrayList(items)

    fun asListOfUri(): List<Uri> {
        val uris = arrayListOf<Uri>()
        for (item in items) {
            uris.add(item.getContentUri())
        }
        return uris
    }

    fun asListOfString(): List<String> {
        val paths = ArrayList<String>()
        items.forEach {
            val path = getPath(context, it.getContentUri())
            if (path != null) paths.add(path)
        }

        return paths
    }

    fun isAcceptable(item: Item?): IncapableCause? {
        if (maxSelectableReached(item)) {
            val maxSelectable = currentMaxSelectable(item)
            val maxSelectableTips = currentMaxSelectableTips(item)

            val cause = try {
                context.getString(maxSelectableTips, maxSelectable)
            } catch (e: Resources.NotFoundException) {
                context.getString(maxSelectableTips, maxSelectable)
            } catch (e: NoClassDefFoundError) {
                context.getString(maxSelectableTips, maxSelectable)
            }

            return IncapableCause(cause)
        } else if (typeConflict(item)) {
            return IncapableCause(context.getString(R.string.error_type_conflict))
        }

        return PhotoMetadataUtils.isAcceptable(context, item)
    }

    private fun currentMaxSelectableTips(item: Item?): Int {
        if (!spec.isMediaTypeExclusive()) {
            if (item?.isImage() == true) {
                return R.string.error_over_count_of_image
            } else if (item?.isVideo() == true) {
                return R.string.error_over_count_of_video
            }
        }

        return R.string.error_over_count
    }

    fun maxSelectableReached(item: Item?): Boolean {
        if (!spec.isMediaTypeExclusive()) {
            if (item?.isImage() == true) {
                return spec.maxImageSelectable == imageItems?.size
            } else if (item?.isVideo() == true) {
                return spec.maxVideoSelectable == videoItems?.size
            }
        }
        return spec.maxSelectable == items.size
    }

    // depends
    private fun currentMaxSelectable(item: Item?): Int {
        if (!spec.isMediaTypeExclusive()) {
            if (item?.isImage() == true) {
                return spec.maxImageSelectable
            } else if (item?.isVideo() == true) {
                return spec.maxVideoSelectable
            }
        }

        return spec.maxSelectable
    }

    fun getCollectionType() = collectionType

    fun isEmpty() = items.isEmpty()

    fun isSelected(item: Item?) = items.contains(item)

    fun count() = items.size

    fun items() = items.toList()

    /**
     * 注:
     * 此处取的是item在选中集合中的序号,
     * 所以不需区分混合选择或单独选择
     */
    fun checkedNumOf(item: Item?): Int {
        val index = ArrayList(items).indexOf(item)
        return if (index == -1) CheckView.UNCHECKED else index + 1
    }

    /**
     * 根据item集合数据设置collectionType
     */
    private fun refineCollectionType() {
        val hasImage = imageItems != null && imageItems?.size ?: 0 > 0
        val hasVideo = videoItems != null && videoItems?.size ?: 0 > 0

        collectionType = if (hasImage && hasVideo) {
            COLLECTION_MIXED
        } else if (hasImage) {
            COLLECTION_IMAGE
        } else if (hasVideo) {
            COLLECTION_VIDEO
        } else {
            COLLECTION_UNDEFINED
        }
    }

    /**
     * Determine whether there will be conflict media types. A user can only select images and videos at the same time
     * while [SelectionSpec.mediaTypeExclusive] is set to false.
     */
    private fun typeConflict(item: Item?) =
        spec.isMediaTypeExclusive()
                && ((item?.isImage() == true && (collectionType == COLLECTION_VIDEO || collectionType == COLLECTION_MIXED))
                || (item?.isVideo() == true && (collectionType == COLLECTION_IMAGE || collectionType == COLLECTION_MIXED)))

    fun add(item: Item?): Boolean {
        if (typeConflict(item)) {
            throw IllegalArgumentException("Can't select images and videos at the same time.")
        }
        if (item == null) return false

        val added = items.add(item)
        addImageOrVideoItem(item)
        if (added) {
            when (collectionType) {
                COLLECTION_UNDEFINED -> {
                    if (item.isImage()) {
                        collectionType = COLLECTION_IMAGE
                    } else if (item.isVideo()) {
                        collectionType = COLLECTION_VIDEO
                    }
                }

                COLLECTION_IMAGE, COLLECTION_VIDEO -> {
                    if ((item.isImage() && collectionType == COLLECTION_VIDEO)
                        || item.isVideo() && collectionType == COLLECTION_IMAGE
                    ) {
                        collectionType = COLLECTION_MIXED
                    }
                }
            }
        }

        return added
    }

    private fun addImageOrVideoItem(item: Item) {
        if (item.isImage()) {
            if (imageItems == null)
                imageItems = linkedSetOf()

            imageItems?.add(item)
        } else if (item.isVideo()) {
            if (videoItems == null)
                videoItems = linkedSetOf()

            videoItems?.add(item)
        }
    }

    private fun removeImageOrVideoItem(item: Item) {
        if (item.isImage()) {
            imageItems?.remove(item)
        } else if (item.isVideo()) {
            videoItems?.remove(item)
        }
    }

    fun remove(item: Item?): Boolean {
        if (item == null) return false
        val removed = items.remove(item)
        removeImageOrVideoItem(item)
        if (removed) resetType()
        return removed
    }

    fun removeAll() {
        items.clear()
        imageItems?.clear()
        videoItems?.clear()
        resetType()
    }
}

================================================
FILE: matisse/src/main/java/com/matisse/photoview/Compat.java
================================================
/*******************************************************************************
 * Copyright 2011, 2012 Chris Banes.
 *
 * 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.
 *******************************************************************************/
package com.matisse.photoview;

import android.annotation.TargetApi;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.view.View;

class Compat {

    private static final int SIXTY_FPS_INTERVAL = 1000 / 60;

    public static void postOnAnimation(View view, Runnable runnable) {
        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
            postOnAnimationJellyBean(view, runnable);
        } else {
            view.postDelayed(runnable, SIXTY_FPS_INTERVAL);
        }
    }

    @TargetApi(16)
    private static void postOnAnimationJellyBean(View view, Runnable runnable) {
        view.postOnAnimation(runnable);
    }
}


================================================
FILE: matisse/src/main/java/com/matisse/photoview/CustomGestureDetector.java
================================================
/*******************************************************************************
 * Copyright 2011, 2012 Chris Banes.
 * <p/>
 * 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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.
 *******************************************************************************/
package com.matisse.photoview;

import android.content.Context;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;

/**
 * Does a whole lot of gesture detecting.
 */
class CustomGestureDetector {

    private static final int INVALID_POINTER_ID = -1;

    private int mActivePointerId = INVALID_POINTER_ID;
    private int mActivePointerIndex = 0;
    private final ScaleGestureDetector mDetector;

    private VelocityTracker mVelocityTracker;
    private boolean mIsDragging;
    private float mLastTouchX;
    private float mLastTouchY;
    private final float mTouchSlop;
    private final float mMinimumVelocity;
    private OnGestureListener mListener;

    CustomGestureDetector(Context context, OnGestureListener listener) {
        final ViewConfiguration configuration = ViewConfiguration
                .get(context);
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
        mTouchSlop = configuration.getScaledTouchSlop();

        mListener = listener;
        ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {

            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                float scaleFactor = detector.getScaleFactor();

                if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
                    return false;

                if (scaleFactor >= 0) {
                    mListener.onScale(scaleFactor,
                            detector.getFocusX(), detector.getFocusY());
                }
                return true;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector detector) {
                // NO-OP
            }
        };
        mDetector = new ScaleGestureDetector(context, mScaleListener);
    }

    private float getActiveX(MotionEvent ev) {
        try {
            return ev.getX(mActivePointerIndex);
        } catch (Exception e) {
            return ev.getX();
        }
    }

    private float getActiveY(MotionEvent ev) {
        try {
            return ev.getY(mActivePointerIndex);
        } catch (Exception e) {
            return ev.getY();
        }
    }

    public boolean isScaling() {
        return mDetector.isInProgress();
    }

    public boolean isDragging() {
        return mIsDragging;
    }

    public boolean onTouchEvent(MotionEvent ev) {
        try {
            mDetector.onTouchEvent(ev);
            return processTouchEvent(ev);
        } catch (IllegalArgumentException e) {
            // Fix for support lib bug, happening when onDestroy is called
            return true;
        }
    }

    private boolean processTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mActivePointerId = ev.getPointerId(0);

                mVelocityTracker = VelocityTracker.obtain();
                if (null != mVelocityTracker) {
                    mVelocityTracker.addMovement(ev);
                }

                mLastTouchX = getActiveX(ev);
                mLastTouchY = getActiveY(ev);
                mIsDragging = false;
                break;
            case MotionEvent.ACTION_MOVE:
                final float x = getActiveX(ev);
                final float y = getActiveY(ev);
                final float dx = x - mLastTouchX, dy = y - mLastTouchY;

                if (!mIsDragging) {
                    // Use Pythagoras to see if drag length is larger than
                    // touch slop
                    mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
                }

                if (mIsDragging) {
                    mListener.onDrag(dx, dy);
                    mLastTouchX = x;
                    mLastTouchY = y;

                    if (null != mVelocityTracker) {
                        mVelocityTracker.addMovement(ev);
                    }
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                mActivePointerId = INVALID_POINTER_ID;
                // Recycle Velocity Tracker
                if (null != mVelocityTracker) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
            case MotionEvent.ACTION_UP:
                mActivePointerId = INVALID_POINTER_ID;
                if (mIsDragging) {
                    if (null != mVelocityTracker) {
                        mLastTouchX = getActiveX(ev);
                        mLastTouchY = getActiveY(ev);

                        // Compute velocity within the last 1000ms
                        mVelocityTracker.addMovement(ev);
                        mVelocityTracker.computeCurrentVelocity(1000);

                        final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker
                                .getYVelocity();

                        // If the velocity is greater than minVelocity, call
                        // listener
                        if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
                            mListener.onFling(mLastTouchX, mLastTouchY, -vX,
                                    -vY);
                        }
                    }
                }

                // Recycle Velocity Tracker
                if (null != mVelocityTracker) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                final int pointerIndex = Util.getPointerIndex(ev.getAction());
                final int pointerId = ev.getPointerId(pointerIndex);
                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                    mLastTouchX = ev.getX(newPointerIndex);
                    mLastTouchY = ev.getY(newPointerIndex);
                }
                break;
        }

        mActivePointerIndex = ev
                .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId
                        : 0);
        return true;
    }
}


================================================
FILE: matisse/src/main/java/com/matisse/photoview/OnGestureListener.java
================================================
/*******************************************************************************
 * Copyright 2011, 2012 Chris Banes.
 *
 * 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.
 *******************************************************************************/
package com.matisse.photoview;

interface OnGestureListener {

    void onDrag(float dx, float dy);

    void onFling(float startX, float startY, float velocityX,
                 float velocityY);

    void onScale(float scaleFactor, float focusX, float focusY);

}

================================================
FILE: matisse/src/main/java/com/matisse/photoview/OnMatrixChangedListener.java
================================================
package com.matisse.photoview;

import android.graphics.RectF;

/**
 * Interface definition for a callback to be invoked when the internal Matrix has changed for
 * this View.
 */
public interface OnMatrixChangedListener {

    /**
     * Callback for when the Matrix displaying the Drawable has changed. This could be because
     * the View's bounds have changed, or the user has zoomed.
     *
     * @param rect - Rectangle displaying the Drawable's new bounds.
     */
    void onMatrixChanged(RectF rect);
}


================================================
FILE: matisse/src/main/java/com/matisse/photoview/OnOutsidePhotoTapListener.java
================================================
package com.matisse.photoview;

import android.widget.ImageView;

/**
 * Callback when the user tapped outside of the photo
 */
public interface OnOutsidePhotoTapListener {

    /**
     * The outside of the photo has been tapped
     */
    void onOutsidePhotoTap(ImageView imageView);
}


================================================
FILE: matisse/src/main/java/com/matisse/photoview/OnPhotoTapListener.java
================================================
package com.matisse.photoview;

import android.widget.ImageView;

/**
 * A callback to be invoked when the Photo is tapped with a single
 * tap.
 */
public interface OnPhotoTapListener {

    /**
     * A callback to receive where the user taps on a photo. You will only receive a callback if
     * the user taps on the actual photo, tapping on 'whitespace' will be ignored.
     *
     * @param view ImageView the user tapped.
     * @param x    where the user tapped from the of the Drawable, as percentage of the
     *             Drawable width.
     * @param y    where the user tapped from the top of the Drawable, as percentage of the
     *             Drawable height.
     */
    void onPhotoTap(ImageView view, float x, float y);
}


================================================
FILE: matisse/src/main/java/com/matisse/photoview/OnScaleChangedListener.java
================================================
package com.matisse.photoview;


/**
 * Interface definition for callback to be invoked when attached ImageView scale changes
 */
public interface OnScaleChangedListener {

    /**
     * Callback for when the scale changes
     *
     * @param scaleFactor the scale factor 
Download .txt
gitextract_to5l3p13/

├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── leo/
│       │               └── matisse/
│       │                   └── ExampleInstrumentedTest.kt
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── leo/
│       │   │           └── matisse/
│       │   │               ├── ExampleActivity.kt
│       │   │               ├── FrescoEngine.kt
│       │   │               ├── Glide4Engine.kt
│       │   │               ├── GlideEngine.kt
│       │   │               ├── ImageSizeFilter.kt
│       │   │               ├── MainActivity.kt
│       │   │               └── SizeFilter.kt
│       │   └── res/
│       │       ├── drawable/
│       │       │   └── ic_launcher_background.xml
│       │       ├── drawable-v24/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── drawable-xhdpi/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── layout/
│       │       │   ├── activity_example.xml
│       │       │   └── activity_main.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       ├── values/
│       │       │   ├── colors.xml
│       │       │   ├── colors_dracula.xml
│       │       │   ├── ids.xml
│       │       │   ├── strings.xml
│       │       │   └── styles.xml
│       │       └── xml/
│       │           └── file_paths_public.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── leo/
│                       └── matisse/
│                           └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
├── matisse/
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── matisse/
│       │               └── ExampleInstrumentedTest.java
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── matisse/
│       │   │           ├── Matisse.kt
│       │   │           ├── MimeType.kt
│       │   │           ├── MimeTypeManager.kt
│       │   │           ├── SelectionCreator.kt
│       │   │           ├── engine/
│       │   │           │   └── ImageEngine.kt
│       │   │           ├── entity/
│       │   │           │   ├── Album.kt
│       │   │           │   ├── CaptureStrategy.kt
│       │   │           │   ├── ConstValue.kt
│       │   │           │   ├── IncapableCause.kt
│       │   │           │   └── Item.kt
│       │   │           ├── filter/
│       │   │           │   └── Filter.kt
│       │   │           ├── internal/
│       │   │           │   └── entity/
│       │   │           │       └── SelectionSpec.kt
│       │   │           ├── listener/
│       │   │           │   ├── OnCheckedListener.kt
│       │   │           │   └── OnSelectedListener.kt
│       │   │           ├── loader/
│       │   │           │   ├── AlbumLoader.kt
│       │   │           │   └── AlbumMediaLoader.kt
│       │   │           ├── model/
│       │   │           │   ├── AlbumCallbacks.kt
│       │   │           │   ├── AlbumCollection.kt
│       │   │           │   ├── AlbumMediaCollection.kt
│       │   │           │   └── SelectedItemCollection.kt
│       │   │           ├── photoview/
│       │   │           │   ├── Compat.java
│       │   │           │   ├── CustomGestureDetector.java
│       │   │           │   ├── OnGestureListener.java
│       │   │           │   ├── OnMatrixChangedListener.java
│       │   │           │   ├── OnOutsidePhotoTapListener.java
│       │   │           │   ├── OnPhotoTapListener.java
│       │   │           │   ├── OnScaleChangedListener.java
│       │   │           │   ├── OnSingleFlingListener.java
│       │   │           │   ├── OnViewDragListener.java
│       │   │           │   ├── OnViewTapListener.java
│       │   │           │   ├── PhotoView.java
│       │   │           │   ├── PhotoViewAttacher.java
│       │   │           │   └── Util.java
│       │   │           ├── ucrop/
│       │   │           │   ├── PictureMultiCuttingActivity.java
│       │   │           │   ├── PicturePhotoGalleryAdapter.java
│       │   │           │   ├── UCrop.java
│       │   │           │   ├── UCropActivity.java
│       │   │           │   ├── UCropMulti.java
│       │   │           │   ├── callback/
│       │   │           │   │   └── Callback.kt
│       │   │           │   ├── immersion/
│       │   │           │   │   ├── CropImmersiveManage.java
│       │   │           │   │   ├── CropLightStatusBarUtils.java
│       │   │           │   │   └── CropRomUtils.java
│       │   │           │   ├── model/
│       │   │           │   │   ├── AspectRatio.java
│       │   │           │   │   ├── CropParameters.java
│       │   │           │   │   ├── CutInfo.java
│       │   │           │   │   ├── ExifInfo.java
│       │   │           │   │   └── ImageState.java
│       │   │           │   ├── task/
│       │   │           │   │   ├── BitmapCropTask.java
│       │   │           │   │   ├── BitmapLoadShowTask.java
│       │   │           │   │   └── BitmapLoadTask.java
│       │   │           │   ├── util/
│       │   │           │   │   ├── BitmapLoadUtils.java
│       │   │           │   │   ├── CubicEasing.java
│       │   │           │   │   ├── EglUtils.java
│       │   │           │   │   ├── FastBitmapDrawable.java
│       │   │           │   │   ├── FileUtils.java
│       │   │           │   │   ├── ImageHeaderParser.java
│       │   │           │   │   ├── RectUtils.java
│       │   │           │   │   ├── RotationGestureDetector.java
│       │   │           │   │   ├── SelectedStateListDrawable.java
│       │   │           │   │   └── VersionUtils.java
│       │   │           │   └── view/
│       │   │           │       ├── CropImageView.java
│       │   │           │       ├── GestureCropImageView.java
│       │   │           │       ├── OverlayView.java
│       │   │           │       ├── TransformImageView.java
│       │   │           │       ├── UCropView.java
│       │   │           │       └── widget/
│       │   │           │           ├── AspectRatioTextView.java
│       │   │           │           └── HorizontalProgressWheelView.java
│       │   │           ├── ui/
│       │   │           │   ├── activity/
│       │   │           │   │   ├── AlbumPreviewActivity.kt
│       │   │           │   │   ├── BaseActivity.kt
│       │   │           │   │   ├── BasePreviewActivity.kt
│       │   │           │   │   ├── SelectedPreviewActivity.kt
│       │   │           │   │   └── matisse/
│       │   │           │   │       ├── AlbumFolderSheetHelper.kt
│       │   │           │   │       ├── AlbumLoadHelper.kt
│       │   │           │   │       ├── IAlbumLoad.kt
│       │   │           │   │       └── MatisseActivity.kt
│       │   │           │   ├── adapter/
│       │   │           │   │   ├── AlbumMediaAdapter.kt
│       │   │           │   │   ├── FolderItemMediaAdapter.kt
│       │   │           │   │   ├── PicturePreviewPagerAdapter.kt
│       │   │           │   │   ├── PreviewPagerAdapter.kt
│       │   │           │   │   └── RecyclerViewCursorAdapter.kt
│       │   │           │   └── view/
│       │   │           │       ├── BottomSheetDialogFragment.kt
│       │   │           │       ├── FolderBottomSheet.kt
│       │   │           │       ├── MediaSelectionFragment.kt
│       │   │           │       ├── PicturePreviewItemFragment.kt
│       │   │           │       └── PreviewItemFragment.kt
│       │   │           ├── utils/
│       │   │           │   ├── BitmapUtils.kt
│       │   │           │   ├── ExifInterfaceCompat.kt
│       │   │           │   ├── IntentUtils.kt
│       │   │           │   ├── ItemSelectUtils.kt
│       │   │           │   ├── MediaStoreCompat.kt
│       │   │           │   ├── PathUtils.kt
│       │   │           │   ├── PhotoMetadataUtils.kt
│       │   │           │   ├── Platform.kt
│       │   │           │   └── UIUtils.kt
│       │   │           └── widget/
│       │   │               ├── CheckRadioView.kt
│       │   │               ├── CheckView.kt
│       │   │               ├── IncapableDialog.kt
│       │   │               ├── MediaGrid.kt
│       │   │               ├── MediaGridInset.kt
│       │   │               ├── PreviewViewPager.kt
│       │   │               ├── SquareFrameLayout.kt
│       │   │               └── longimage/
│       │   │                   ├── CompatDecoderFactory.java
│       │   │                   ├── DecoderFactory.java
│       │   │                   ├── ImageDecoder.java
│       │   │                   ├── ImageRegionDecoder.java
│       │   │                   ├── ImageSource.java
│       │   │                   ├── ImageViewState.java
│       │   │                   ├── SkiaImageDecoder.java
│       │   │                   ├── SkiaImageRegionDecoder.java
│       │   │                   └── SubsamplingScaleImageView.java
│       │   └── res/
│       │       ├── anim/
│       │       │   ├── bottom_down_out.xml
│       │       │   ├── bottom_up_in.xml
│       │       │   ├── ucrop_anim_fade_in.xml
│       │       │   ├── ucrop_close.xml
│       │       │   ├── ucrop_loader_circle_path.xml
│       │       │   └── ucrop_loader_circle_scale.xml
│       │       ├── color/
│       │       │   ├── selector_base_text.xml
│       │       │   ├── selector_black_text.xml
│       │       │   ├── selector_white_text.xml
│       │       │   └── ucrop_scale_text_view_selector.xml
│       │       ├── drawable/
│       │       │   ├── ucrop_gif_bg.xml
│       │       │   ├── ucrop_oval_true.xml
│       │       │   ├── ucrop_shadow_upside.xml
│       │       │   ├── ucrop_vector_ic_crop.xml
│       │       │   ├── ucrop_vector_loader.xml
│       │       │   └── ucrop_vector_loader_animated.xml
│       │       ├── drawable-xhdpi/
│       │       │   └── transparent.xml
│       │       ├── layout/
│       │       │   ├── activity_matisse.xml
│       │       │   ├── activity_media_preview.xml
│       │       │   ├── dialog_bottom_sheet.xml
│       │       │   ├── dialog_bottom_sheet_folder.xml
│       │       │   ├── fragment_media_selection.xml
│       │       │   ├── fragment_picture_preview_item.xml
│       │       │   ├── fragment_preview_item.xml
│       │       │   ├── include_view_bottom.xml
│       │       │   ├── include_view_navigation.xml
│       │       │   ├── item_album_folder.xml
│       │       │   ├── item_media_grid.xml
│       │       │   ├── item_photo_capture.xml
│       │       │   ├── ucrop_activity_photobox.xml
│       │       │   ├── ucrop_aspect_ratio.xml
│       │       │   ├── ucrop_layout_rotate_wheel.xml
│       │       │   ├── ucrop_layout_scale_wheel.xml
│       │       │   ├── ucrop_picture_activity_multi_cutting.xml
│       │       │   ├── ucrop_picture_gf_adapter_edit_list.xml
│       │       │   ├── ucrop_view.xml
│       │       │   └── view_media_grid_content.xml
│       │       ├── menu/
│       │       │   └── ucrop_menu_activity.xml
│       │       ├── values/
│       │       │   ├── attrs.xml
│       │       │   ├── colors.xml
│       │       │   ├── colors_default.xml
│       │       │   ├── dimens.xml
│       │       │   ├── ids.xml
│       │       │   ├── long_attrs.xml
│       │       │   ├── strings.xml
│       │       │   ├── styles.xml
│       │       │   └── values.xml
│       │       └── values-zh/
│       │           └── strings.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── matisse/
│                       └── ExampleUnitTest.java
└── settings.gradle
Download .txt
SYMBOL INDEX (924 symbols across 57 files)

FILE: matisse/src/androidTest/java/com/matisse/ExampleInstrumentedTest.java
  class ExampleInstrumentedTest (line 17) | @RunWith(AndroidJUnit4.class)
    method useAppContext (line 19) | @Test

FILE: matisse/src/main/java/com/matisse/photoview/Compat.java
  class Compat (line 23) | class Compat {
    method postOnAnimation (line 27) | public static void postOnAnimation(View view, Runnable runnable) {
    method postOnAnimationJellyBean (line 35) | @TargetApi(16)

FILE: matisse/src/main/java/com/matisse/photoview/CustomGestureDetector.java
  class CustomGestureDetector (line 27) | class CustomGestureDetector {
    method CustomGestureDetector (line 43) | CustomGestureDetector(Context context, OnGestureListener listener) {
    method getActiveX (line 79) | private float getActiveX(MotionEvent ev) {
    method getActiveY (line 87) | private float getActiveY(MotionEvent ev) {
    method isScaling (line 95) | public boolean isScaling() {
    method isDragging (line 99) | public boolean isDragging() {
    method onTouchEvent (line 103) | public boolean onTouchEvent(MotionEvent ev) {
    method processTouchEvent (line 113) | private boolean processTouchEvent(MotionEvent ev) {

FILE: matisse/src/main/java/com/matisse/photoview/OnGestureListener.java
  type OnGestureListener (line 18) | interface OnGestureListener {
    method onDrag (line 20) | void onDrag(float dx, float dy);
    method onFling (line 22) | void onFling(float startX, float startY, float velocityX,
    method onScale (line 25) | void onScale(float scaleFactor, float focusX, float focusY);

FILE: matisse/src/main/java/com/matisse/photoview/OnMatrixChangedListener.java
  type OnMatrixChangedListener (line 9) | public interface OnMatrixChangedListener {
    method onMatrixChanged (line 17) | void onMatrixChanged(RectF rect);

FILE: matisse/src/main/java/com/matisse/photoview/OnOutsidePhotoTapListener.java
  type OnOutsidePhotoTapListener (line 8) | public interface OnOutsidePhotoTapListener {
    method onOutsidePhotoTap (line 13) | void onOutsidePhotoTap(ImageView imageView);

FILE: matisse/src/main/java/com/matisse/photoview/OnPhotoTapListener.java
  type OnPhotoTapListener (line 9) | public interface OnPhotoTapListener {
    method onPhotoTap (line 21) | void onPhotoTap(ImageView view, float x, float y);

FILE: matisse/src/main/java/com/matisse/photoview/OnScaleChangedListener.java
  type OnScaleChangedListener (line 7) | public interface OnScaleChangedListener {
    method onScaleChange (line 16) | void onScaleChange(float scaleFactor, float focusX, float focusY);

FILE: matisse/src/main/java/com/matisse/photoview/OnSingleFlingListener.java
  type OnSingleFlingListener (line 9) | public interface OnSingleFlingListener {
    method onFling (line 20) | boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float...

FILE: matisse/src/main/java/com/matisse/photoview/OnViewDragListener.java
  type OnViewDragListener (line 6) | public interface OnViewDragListener {
    method onDrag (line 15) | void onDrag(float dx, float dy);

FILE: matisse/src/main/java/com/matisse/photoview/OnViewTapListener.java
  type OnViewTapListener (line 5) | public interface OnViewTapListener {
    method onViewTap (line 15) | void onViewTap(View view, float x, float y);

FILE: matisse/src/main/java/com/matisse/photoview/PhotoView.java
  class PhotoView (line 32) | public class PhotoView extends AppCompatImageView {
    method PhotoView (line 37) | public PhotoView(Context context) {
    method PhotoView (line 41) | public PhotoView(Context context, AttributeSet attr) {
    method PhotoView (line 45) | public PhotoView(Context context, AttributeSet attr, int defStyle) {
    method init (line 50) | private void init() {
    method getAttacher (line 69) | public PhotoViewAttacher getAttacher() {
    method getScaleType (line 73) | @Override
    method getImageMatrix (line 78) | @Override
    method setOnLongClickListener (line 83) | @Override
    method setOnClickListener (line 88) | @Override
    method setScaleType (line 93) | @Override
    method setImageDrawable (line 102) | @Override
    method setImageResource (line 111) | @Override
    method setImageURI (line 119) | @Override
    method setFrame (line 127) | @Override
    method setRotationTo (line 136) | public void setRotationTo(float rotationDegree) {
    method setRotationBy (line 140) | public void setRotationBy(float rotationDegree) {
    method isZoomable (line 144) | public boolean isZoomable() {
    method setZoomable (line 148) | public void setZoomable(boolean zoomable) {
    method getDisplayRect (line 152) | public RectF getDisplayRect() {
    method getDisplayMatrix (line 156) | public void getDisplayMatrix(Matrix matrix) {
    method setDisplayMatrix (line 160) | @SuppressWarnings("UnusedReturnValue")
    method getSuppMatrix (line 165) | public void getSuppMatrix(Matrix matrix) {
    method setSuppMatrix (line 169) | public boolean setSuppMatrix(Matrix matrix) {
    method getMinimumScale (line 173) | public float getMinimumScale() {
    method getMediumScale (line 177) | public float getMediumScale() {
    method getMaximumScale (line 181) | public float getMaximumScale() {
    method getScale (line 185) | public float getScale() {
    method setAllowParentInterceptOnEdge (line 189) | public void setAllowParentInterceptOnEdge(boolean allow) {
    method setMinimumScale (line 193) | public void setMinimumScale(float minimumScale) {
    method setMediumScale (line 197) | public void setMediumScale(float mediumScale) {
    method setMaximumScale (line 201) | public void setMaximumScale(float maximumScale) {
    method setScaleLevels (line 205) | public void setScaleLevels(float minimumScale, float mediumScale, floa...
    method setOnMatrixChangeListener (line 209) | public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
    method setOnPhotoTapListener (line 213) | public void setOnPhotoTapListener(OnPhotoTapListener listener) {
    method setOnOutsidePhotoTapListener (line 217) | public void setOnOutsidePhotoTapListener(OnOutsidePhotoTapListener lis...
    method setOnViewTapListener (line 221) | public void setOnViewTapListener(OnViewTapListener listener) {
    method setOnViewDragListener (line 225) | public void setOnViewDragListener(OnViewDragListener listener) {
    method setScale (line 229) | public void setScale(float scale) {
    method setScale (line 233) | public void setScale(float scale, boolean animate) {
    method setScale (line 237) | public void setScale(float scale, float focalX, float focalY, boolean ...
    method setZoomTransitionDuration (line 241) | public void setZoomTransitionDuration(int milliseconds) {
    method setOnDoubleTapListener (line 245) | public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener...
    method setOnScaleChangeListener (line 249) | public void setOnScaleChangeListener(OnScaleChangedListener onScaleCha...
    method setOnSingleFlingListener (line 253) | public void setOnSingleFlingListener(OnSingleFlingListener onSingleFli...

FILE: matisse/src/main/java/com/matisse/photoview/PhotoViewAttacher.java
  class PhotoViewAttacher (line 39) | public class PhotoViewAttacher implements View.OnTouchListener,
    method onDrag (line 99) | @Override
    method onFling (line 137) | @Override
    method onScale (line 145) | @Override
    method PhotoViewAttacher (line 157) | public PhotoViewAttacher(ImageView imageView) {
    method setOnDoubleTapListener (line 251) | public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener...
    method setOnScaleChangeListener (line 255) | public void setOnScaleChangeListener(OnScaleChangedListener onScaleCha...
    method setOnSingleFlingListener (line 259) | public void setOnSingleFlingListener(OnSingleFlingListener onSingleFli...
    method isZoomEnabled (line 263) | @Deprecated
    method getDisplayRect (line 268) | public RectF getDisplayRect() {
    method setDisplayMatrix (line 273) | public boolean setDisplayMatrix(Matrix finalMatrix) {
    method setBaseRotation (line 285) | public void setBaseRotation(final float degrees) {
    method setRotationTo (line 292) | public void setRotationTo(float degrees) {
    method setRotationBy (line 297) | public void setRotationBy(float degrees) {
    method getMinimumScale (line 302) | public float getMinimumScale() {
    method getMediumScale (line 306) | public float getMediumScale() {
    method getMaximumScale (line 310) | public float getMaximumScale() {
    method getScale (line 314) | public float getScale() {
    method getScaleType (line 319) | public ScaleType getScaleType() {
    method onLayoutChange (line 323) | @Override
    method onTouch (line 332) | @Override
    method setAllowParentInterceptOnEdge (line 387) | public void setAllowParentInterceptOnEdge(boolean allow) {
    method setMinimumScale (line 391) | public void setMinimumScale(float minimumScale) {
    method setMediumScale (line 396) | public void setMediumScale(float mediumScale) {
    method setMaximumScale (line 401) | public void setMaximumScale(float maximumScale) {
    method setScaleLevels (line 406) | public void setScaleLevels(float minimumScale, float mediumScale, floa...
    method setOnLongClickListener (line 413) | public void setOnLongClickListener(OnLongClickListener listener) {
    method setOnClickListener (line 417) | public void setOnClickListener(View.OnClickListener listener) {
    method setOnMatrixChangeListener (line 421) | public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
    method setOnPhotoTapListener (line 425) | public void setOnPhotoTapListener(OnPhotoTapListener listener) {
    method setOnOutsidePhotoTapListener (line 429) | public void setOnOutsidePhotoTapListener(OnOutsidePhotoTapListener mOu...
    method setOnViewTapListener (line 433) | public void setOnViewTapListener(OnViewTapListener listener) {
    method setOnViewDragListener (line 437) | public void setOnViewDragListener(OnViewDragListener listener) {
    method setScale (line 441) | public void setScale(float scale) {
    method setScale (line 445) | public void setScale(float scale, boolean animate) {
    method setScale (line 452) | public void setScale(float scale, float focalX, float focalY,
    method setZoomInterpolator (line 472) | public void setZoomInterpolator(Interpolator interpolator) {
    method setScaleType (line 476) | public void setScaleType(ScaleType scaleType) {
    method isZoomable (line 483) | public boolean isZoomable() {
    method setZoomable (line 487) | public void setZoomable(boolean zoomable) {
    method update (line 492) | public void update() {
    method getDisplayMatrix (line 507) | public void getDisplayMatrix(Matrix matrix) {
    method getSuppMatrix (line 514) | public void getSuppMatrix(Matrix matrix) {
    method getDrawMatrix (line 518) | private Matrix getDrawMatrix() {
    method getImageMatrix (line 524) | public Matrix getImageMatrix() {
    method setZoomTransitionDuration (line 528) | public void setZoomTransitionDuration(int milliseconds) {
    method getValue (line 539) | private float getValue(Matrix matrix, int whichValue) {
    method resetMatrix (line 547) | private void resetMatrix() {
    method setImageViewMatrix (line 554) | private void setImageViewMatrix(Matrix matrix) {
    method checkAndDisplayMatrix (line 568) | private void checkAndDisplayMatrix() {
    method getDisplayRect (line 580) | private RectF getDisplayRect(Matrix matrix) {
    method updateBaseMatrix (line 596) | private void updateBaseMatrix(Drawable drawable) {
    method checkMatrixBounds (line 649) | private boolean checkMatrixBounds() {
    method getImageViewWidth (line 707) | private int getImageViewWidth(ImageView imageView) {
    method getImageViewHeight (line 711) | private int getImageViewHeight(ImageView imageView) {
    method cancelFling (line 715) | private void cancelFling() {
    class AnimatedZoomRunnable (line 722) | private class AnimatedZoomRunnable implements Runnable {
      method AnimatedZoomRunnable (line 728) | public AnimatedZoomRunnable(final float currentZoom, final float tar...
      method run (line 737) | @Override
      method interpolate (line 749) | private float interpolate() {
    class FlingRunnable (line 757) | private class FlingRunnable implements Runnable {
      method FlingRunnable (line 762) | public FlingRunnable(Context context) {
      method cancelFling (line 766) | public void cancelFling() {
      method fling (line 770) | public void fling(int viewWidth, int viewHeight, int velocityX,
      method run (line 800) | @Override

FILE: matisse/src/main/java/com/matisse/photoview/Util.java
  class Util (line 6) | class Util {
    method checkZoomLevels (line 8) | static void checkZoomLevels(float minZoom, float midZoom,
    method hasDrawable (line 19) | static boolean hasDrawable(ImageView imageView) {
    method isSupportedScaleType (line 23) | static boolean isSupportedScaleType(final ImageView.ScaleType scaleTyp...
    method getPointerIndex (line 34) | static int getPointerIndex(int action) {

FILE: matisse/src/main/java/com/matisse/ucrop/PictureMultiCuttingActivity.java
  class PictureMultiCuttingActivity (line 63) | @SuppressWarnings("ConstantConditions")
    method isImmersive (line 131) | @Override
    method immersive (line 140) | public void immersive() {
    method onCreate (line 147) | @Override
    method initLoadCutData (line 169) | private void initLoadCutData() {
    method addPhotoRecyclerView (line 181) | private void addPhotoRecyclerView() {
    method refreshPhotoRecyclerData (line 213) | private void refreshPhotoRecyclerData() {
    method resetCutDataStatus (line 228) | private void resetCutDataStatus() {
    method onCreateOptionsMenu (line 236) | @Override
    method onPrepareOptionsMenu (line 263) | @Override
    method onOptionsItemSelected (line 270) | @Override
    method onBackPressed (line 280) | @Override
    method onStop (line 286) | @Override
    method setImageData (line 297) | private void setImageData(@NonNull Intent intent) {
    method processOptions (line 325) | @SuppressWarnings("deprecation")
    method getIntentData (line 391) | private void getIntentData(@NonNull Intent intent) {
    method setupViews (line 403) | private void setupViews(@NonNull Intent intent) {
    method setNavBarColor (line 429) | private void setNavBarColor() {
    method setupAppBar (line 441) | private void setupAppBar() {
    method initiateRootViews (line 465) | private void initiateRootViews() {
    method onRotate (line 477) | @Override
    method onScale (line 482) | @Override
    method onLoadComplete (line 487) | @Override
    method onLoadFailure (line 495) | @Override
    method setStatusBarColor (line 508) | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    method setAngleText (line 519) | private void setAngleText(float angle) {
    method setScaleText (line 525) | private void setScaleText(float scale) {
    method resetRotation (line 531) | private void resetRotation() {
    method rotateByAngle (line 536) | private void rotateByAngle(int angle) {
    method setInitialState (line 541) | private void setInitialState() {
    method setAllowedGestures (line 545) | private void setAllowedGestures(int tab) {
    method addBlockingView (line 555) | private void addBlockingView() {
    method cropAndSaveImage (line 566) | protected void cropAndSaveImage() {
    method setResultUri (line 585) | protected void setResultUri(Uri uri, float resultAspectRatio, int offs...
    method resetCutData (line 613) | protected void resetCutData() {
    method changeLayoutParams (line 641) | private void changeLayoutParams() {
    method getLastImgType (line 658) | public static String getLastImgType(String path) {
    method setResultError (line 688) | protected void setResultError(Throwable throwable) {
    method closeActivity (line 695) | protected void closeActivity() {
    method exitAnimation (line 700) | protected void exitAnimation() {
    method dip2px (line 705) | public int dip2px(float dpValue) {

FILE: matisse/src/main/java/com/matisse/ucrop/PicturePhotoGalleryAdapter.java
  class PicturePhotoGalleryAdapter (line 48) | public class PicturePhotoGalleryAdapter extends RecyclerView.Adapter<Pic...
    method PicturePhotoGalleryAdapter (line 56) | public PicturePhotoGalleryAdapter(Context context, List<CutInfo> list) {
    method setData (line 63) | public void setData(List<CutInfo> list) {
    method onCreateViewHolder (line 68) | @Override
    method onBindViewHolder (line 75) | @Override
    method getItemCount (line 118) | @Override
    class ViewHolder (line 124) | public static class ViewHolder extends RecyclerView.ViewHolder {
      method ViewHolder (line 129) | public ViewHolder(View view) {
    method setOnItemClickListener (line 139) | public void setOnItemClickListener(OnItemClickListener listener) {
    type OnItemClickListener (line 143) | public interface OnItemClickListener {
      method onItemClick (line 144) | void onItemClick(int position, View view);

FILE: matisse/src/main/java/com/matisse/ucrop/UCrop.java
  class UCrop (line 33) | public class UCrop {
    method of (line 68) | public static UCrop of(@NonNull Uri source, @NonNull Uri destination) {
    method UCrop (line 72) | private UCrop(@NonNull Uri source, @NonNull Uri destination) {
    method withAspectRatio (line 86) | public UCrop withAspectRatio(float x, float y) {
    method useSourceImageAspectRatio (line 96) | public UCrop useSourceImageAspectRatio() {
    method withMaxResultSize (line 108) | public UCrop withMaxResultSize(@IntRange(from = 100) int width, @IntRa...
    method withOptions (line 114) | public UCrop withOptions(@NonNull Options options) {
    method startAnimation (line 124) | public void startAnimation(@NonNull Activity activity, @AnimRes int ac...
    method start (line 138) | public void start(@NonNull Activity activity, int requestCode, @AnimRe...
    method start (line 148) | public void start(@NonNull Activity activity) {
    method start (line 158) | public void start(@NonNull Activity activity, int requestCode) {
    method start (line 167) | public void start(@NonNull Context context, @NonNull Fragment fragment) {
    method start (line 177) | public void start(@NonNull Context context, @NonNull Fragment fragment...
    method getIntent (line 186) | public Intent getIntent(@NonNull Context context) {
    method getOutput (line 197) | @Nullable
    method getOutputImageWidth (line 207) | public static int getOutputImageWidth(@NonNull Intent intent) {
    method getOutputImageHeight (line 216) | public static int getOutputImageHeight(@NonNull Intent intent) {
    method getOutputCropAspectRatio (line 226) | public static float getOutputCropAspectRatio(@NonNull Intent intent) {
    method getError (line 236) | @Nullable
    class Options (line 246) | public static class Options {
      method Options (line 298) | public Options() {
      method getOptionBundle (line 302) | @NonNull
      method setCompressionFormat (line 310) | public Options setCompressionFormat(@NonNull Bitmap.CompressFormat f...
      method setCompressionQuality (line 318) | public Options setCompressionQuality(@IntRange(from = 0) int compres...
      method setAllowedGestures (line 326) | public Options setAllowedGestures(@UCropActivity.GestureTypes int ta...
      method setMaxScaleMultiplier (line 338) | public Options setMaxScaleMultiplier(@FloatRange(from = 1.0, fromInc...
      method setImageToCropBoundsAnimDuration (line 348) | public Options setImageToCropBoundsAnimDuration(@IntRange(from = 100...
      method setMaxBitmapSize (line 358) | public Options setMaxBitmapSize(@IntRange(from = 100) int maxBitmapS...
      method setDimmedLayerColor (line 366) | public Options setDimmedLayerColor(@ColorInt int color) {
      method setCircleDimmedLayer (line 374) | public Options setCircleDimmedLayer(boolean isCircle) {
      method setShowCropFrame (line 382) | public Options setShowCropFrame(boolean show) {
      method setCropFrameColor (line 390) | public Options setCropFrameColor(@ColorInt int color) {
      method setCropFrameStrokeWidth (line 398) | public Options setCropFrameStrokeWidth(@IntRange(from = 0) int width) {
      method setShowCropGrid (line 406) | public Options setShowCropGrid(boolean show) {
      method setDragFrameEnabled (line 414) | public Options setDragFrameEnabled(boolean isDragFrame) {
      method setCropGridRowCount (line 422) | public Options setCropGridRowCount(@IntRange(from = 0) int count) {
      method setCropGridColumnCount (line 430) | public Options setCropGridColumnCount(@IntRange(from = 0) int count) {
      method setCropGridColor (line 438) | public Options setCropGridColor(@ColorInt int color) {
      method setCropGridStrokeWidth (line 446) | public Options setCropGridStrokeWidth(@IntRange(from = 0) int width) {
      method setToolbarColor (line 454) | public Options setToolbarColor(@ColorInt int color) {
      method setStatusBarColor (line 462) | public Options setStatusBarColor(@ColorInt int color) {
      method setActiveWidgetColor (line 470) | public Options setActiveWidgetColor(@ColorInt int color) {
      method setToolbarWidgetColor (line 478) | public Options setToolbarWidgetColor(@ColorInt int color) {
      method isOpenWhiteStatusBar (line 486) | public Options isOpenWhiteStatusBar(boolean openWhiteStatusBar) {
      method setToolbarTitle (line 494) | public Options setToolbarTitle(@Nullable String text) {
      method setToolbarCancelDrawable (line 502) | public Options setToolbarCancelDrawable(@DrawableRes int drawable) {
      method setToolbarCropDrawable (line 510) | public Options setToolbarCropDrawable(@DrawableRes int drawable) {
      method setLogoColor (line 518) | public Options setLogoColor(@ColorInt int color) {
      method setCutListData (line 526) | public Options setCutListData(ArrayList<String> list) {
      method setFreeStyleCropEnabled (line 534) | public Options setFreeStyleCropEnabled(boolean enabled) {
      method setStatusFont (line 542) | public Options setStatusFont(boolean statusFont) {
      method setAspectRatioOptions (line 553) | public Options setAspectRatioOptions(int selectedByDefault, AspectRa...
      method setRootViewBackgroundColor (line 567) | public Options setRootViewBackgroundColor(@ColorInt int color) {
      method withAspectRatio (line 579) | public Options withAspectRatio(float x, float y) {
      method useSourceImageAspectRatio (line 589) | public Options useSourceImageAspectRatio() {
      method withMaxResultSize (line 601) | public Options withMaxResultSize(int width, int height) {
      method setCropExitAnimation (line 610) | public Options setCropExitAnimation(@AnimRes int activityCropExitAni...
      method setNavBarColor (line 618) | public Options setNavBarColor(@ColorInt int navBarColor) {

FILE: matisse/src/main/java/com/matisse/ucrop/UCropActivity.java
  class UCropActivity (line 38) | @SuppressWarnings("ConstantConditions")
    method getResourceLayoutId (line 63) | @Override
    method configActivity (line 68) | @Override
    method setViewData (line 74) | @Override
    method initListener (line 82) | @Override
    method onClick (line 88) | @Override
    method onBackPressed (line 98) | @Override
    method onStop (line 104) | @Override
    method setImageData (line 115) | private void setImageData(@NonNull Intent intent) {
    method processOptions (line 143) | private void processOptions(@NonNull Intent intent) {
    method setupViews (line 205) | private void setupViews() {
    method initiateRootViews (line 209) | private void initiateRootViews() {
    method onRotate (line 218) | @Override
    method onScale (line 222) | @Override
    method onLoadComplete (line 226) | @Override
    method onLoadFailure (line 233) | @Override
    method addBlockingView (line 246) | private void addBlockingView() {
    method cropAndSaveImage (line 258) | protected void cropAndSaveImage() {
    method setResultUri (line 276) | protected void setResultUri(Uri uri, float resultAspectRatio, int offs...
    method setResultError (line 288) | protected void setResultError(Throwable throwable) {
    method closeActivity (line 295) | protected void closeActivity() {
    method exitAnimation (line 300) | protected void exitAnimation() {

FILE: matisse/src/main/java/com/matisse/ucrop/UCropMulti.java
  class UCropMulti (line 35) | public class UCropMulti {
    method of (line 71) | public static UCropMulti of(@NonNull Uri source, @NonNull Uri destinat...
    method UCropMulti (line 75) | private UCropMulti(@NonNull Uri source, @NonNull Uri destination) {
    method withAspectRatio (line 89) | public UCropMulti withAspectRatio(float x, float y) {
    method useSourceImageAspectRatio (line 99) | public UCropMulti useSourceImageAspectRatio() {
    method withMaxResultSize (line 111) | public UCropMulti withMaxResultSize(@IntRange(from = 100) int width, @...
    method withOptions (line 117) | public UCropMulti withOptions(@NonNull Options options) {
    method startAnimation (line 127) | public void startAnimation(@NonNull Activity activity, @AnimRes int ac...
    method start (line 141) | public void start(@NonNull Activity activity, int requestCode, @AnimRe...
    method start (line 151) | public void start(@NonNull Activity activity) {
    method start (line 161) | public void start(@NonNull Activity activity, int requestCode) {
    method start (line 170) | public void start(@NonNull Context context, @NonNull Fragment fragment) {
    method start (line 180) | public void start(@NonNull Context context, @NonNull Fragment fragment...
    method getIntent (line 189) | public Intent getIntent(@NonNull Context context) {
    method getOutput (line 201) | @Nullable
    method getOutputImageWidth (line 211) | public static int getOutputImageWidth(@NonNull Intent intent) {
    method getOutputImageHeight (line 220) | public static int getOutputImageHeight(@NonNull Intent intent) {
    method getOutputCropAspectRatio (line 230) | public static float getOutputCropAspectRatio(@NonNull Intent intent) {
    method getError (line 240) | @Nullable
    class Options (line 250) | public static class Options {
      method Options (line 306) | public Options() {
      method getOptionBundle (line 310) | @NonNull
      method setCompressionFormat (line 318) | public Options setCompressionFormat(@NonNull Bitmap.CompressFormat f...
      method setCompressionQuality (line 326) | public Options setCompressionQuality(@IntRange(from = 0) int compres...
      method setAllowedGestures (line 334) | public Options setAllowedGestures(@PictureMultiCuttingActivity.Gestu...
      method setMaxScaleMultiplier (line 346) | public Options setMaxScaleMultiplier(@FloatRange(from = 1.0, fromInc...
      method setImageToCropBoundsAnimDuration (line 356) | public Options setImageToCropBoundsAnimDuration(@IntRange(from = 100...
      method setMaxBitmapSize (line 366) | public Options setMaxBitmapSize(@IntRange(from = 100) int maxBitmapS...
      method setDimmedLayerColor (line 374) | public Options setDimmedLayerColor(@ColorInt int color) {
      method setCircleDimmedLayer (line 382) | public Options setCircleDimmedLayer(boolean isCircle) {
      method setShowCropFrame (line 390) | public Options setShowCropFrame(boolean show) {
      method setCropFrameColor (line 398) | public Options setCropFrameColor(@ColorInt int color) {
      method setCropFrameStrokeWidth (line 406) | public Options setCropFrameStrokeWidth(@IntRange(from = 0) int width) {
      method setShowCropGrid (line 414) | public Options setShowCropGrid(boolean show) {
      method setScaleEnabled (line 419) | public Options setScaleEnabled(boolean scaleEnabled) {
      method setRotateEnabled (line 424) | public Options setRotateEnabled(boolean rotateEnabled) {
      method setDragFrameEnabled (line 432) | public Options setDragFrameEnabled(boolean isDragFrame) {
      method setCropGridRowCount (line 440) | public Options setCropGridRowCount(@IntRange(from = 0) int count) {
      method setCropGridColumnCount (line 448) | public Options setCropGridColumnCount(@IntRange(from = 0) int count) {
      method setCropGridColor (line 456) | public Options setCropGridColor(@ColorInt int color) {
      method setCropGridStrokeWidth (line 464) | public Options setCropGridStrokeWidth(@IntRange(from = 0) int width) {
      method setToolbarColor (line 472) | public Options setToolbarColor(@ColorInt int color) {
      method setStatusBarColor (line 480) | public Options setStatusBarColor(@ColorInt int color) {
      method setActiveWidgetColor (line 488) | public Options setActiveWidgetColor(@ColorInt int color) {
      method setToolbarWidgetColor (line 496) | public Options setToolbarWidgetColor(@ColorInt int color) {
      method isOpenWhiteStatusBar (line 504) | public Options isOpenWhiteStatusBar(boolean openWhiteStatusBar) {
      method setToolbarTitle (line 512) | public Options setToolbarTitle(@Nullable String text) {
      method setToolbarCancelDrawable (line 520) | public Options setToolbarCancelDrawable(@DrawableRes int drawable) {
      method setToolbarCropDrawable (line 528) | public Options setToolbarCropDrawable(@DrawableRes int drawable) {
      method setLogoColor (line 536) | public Options setLogoColor(@ColorInt int color) {
      method setCutListData (line 544) | public Options setCutListData(ArrayList<CutInfo> list) {
      method setFreeStyleCropEnabled (line 552) | public Options setFreeStyleCropEnabled(boolean enabled) {
      method setStatusFont (line 560) | @Deprecated
      method setCropExitAnimation (line 569) | public Options setCropExitAnimation(@AnimRes int activityCropExitAni...
      method setNavBarColor (line 577) | public Options setNavBarColor(@ColorInt int navBarColor) {
      method setAspectRatioOptions (line 588) | public Options setAspectRatioOptions(int selectedByDefault, AspectRa...
      method setRootViewBackgroundColor (line 602) | public Options setRootViewBackgroundColor(@ColorInt int color) {
      method withAspectRatio (line 614) | public Options withAspectRatio(float x, float y) {
      method useSourceImageAspectRatio (line 624) | public Options useSourceImageAspectRatio() {
      method withMaxResultSize (line 636) | public Options withMaxResultSize(int width, int height) {

FILE: matisse/src/main/java/com/matisse/ucrop/immersion/CropImmersiveManage.java
  class CropImmersiveManage (line 16) | public class CropImmersiveManage {
    method immersiveUseful (line 20) | public static boolean immersiveUseful() {
    method immersiveAboveAPI23 (line 37) | public static void immersiveAboveAPI23(AppCompatActivity baseActivity,...
    method immersiveAboveAPI23 (line 49) | public static void immersiveAboveAPI23(AppCompatActivity baseActivity,...

FILE: matisse/src/main/java/com/matisse/ucrop/immersion/CropLightStatusBarUtils.java
  class CropLightStatusBarUtils (line 19) | public class CropLightStatusBarUtils {
    method setLightStatusBarAboveAPI23 (line 20) | public static void setLightStatusBarAboveAPI23(Activity activity, bool...
    method setLightStatusBar (line 27) | public static void setLightStatusBar(Activity activity, boolean dark) {
    method setLightStatusBar (line 31) | public static void setLightStatusBar(Activity activity, boolean isMarg...
    method setMIUILightStatusBar (line 57) | private static boolean setMIUILightStatusBar(Activity activity, boolea...
    method setFlymeLightStatusBar (line 76) | private static boolean setFlymeLightStatusBar(Activity activity, boole...
    method setAndroidNativeLightStatusBar (line 110) | @TargetApi(11)
    method initStatusBarStyle (line 171) | private static void initStatusBarStyle(Activity activity, boolean isMa...

FILE: matisse/src/main/java/com/matisse/ucrop/immersion/CropRomUtils.java
  class CropRomUtils (line 17) | public class CropRomUtils {
    class AvailableRomType (line 18) | public class AvailableRomType {
    method getLightStatausBarAvailableRomType (line 28) | public static int getLightStatausBarAvailableRomType() {
    method isFlymeV4OrAbove (line 54) | private static boolean isFlymeV4OrAbove() {
    method getFlymeVersion (line 61) | public static int getFlymeVersion() {
    method isMIUIV6OrAbove (line 80) | private static boolean isMIUIV6OrAbove() {
    method getMIUIVersionCode (line 95) | public static int getMIUIVersionCode() {
    method isAndroid5OrAbove (line 110) | private static boolean isAndroid5OrAbove() {
    method getSystemProperty (line 118) | public static String getSystemProperty(String propName) {
    method stringToInt (line 139) | public static int stringToInt(String str) {

FILE: matisse/src/main/java/com/matisse/ucrop/model/AspectRatio.java
  class AspectRatio (line 11) | public class AspectRatio implements Parcelable {
    method AspectRatio (line 18) | protected AspectRatio(Parcel in) {
    method writeToParcel (line 24) | @Override
    method describeContents (line 31) | @Override
    method createFromParcel (line 37) | @Override
    method newArray (line 42) | @Override
    method getAspectRatioTitle (line 48) | @Nullable
    method getAspectRatioX (line 53) | public float getAspectRatioX() {
    method getAspectRatioY (line 57) | public float getAspectRatioY() {

FILE: matisse/src/main/java/com/matisse/ucrop/model/CropParameters.java
  class CropParameters (line 9) | public class CropParameters {
    method CropParameters (line 20) | public CropParameters(int maxResultImageSizeX, int maxResultImageSizeY,
    method getMaxResultImageSizeX (line 32) | public int getMaxResultImageSizeX() {
    method getMaxResultImageSizeY (line 36) | public int getMaxResultImageSizeY() {
    method getCompressFormat (line 40) | public Bitmap.CompressFormat getCompressFormat() {
    method getCompressQuality (line 44) | public int getCompressQuality() {
    method getImageInputUri (line 48) | public Uri getImageInputUri() {
    method getImageOutputPath (line 52) | public String getImageOutputPath() {
    method getExifInfo (line 56) | public ExifInfo getExifInfo() {

FILE: matisse/src/main/java/com/matisse/ucrop/model/CutInfo.java
  class CutInfo (line 10) | public class CutInfo implements Serializable {
    method CutInfo (line 55) | public CutInfo() {
    method CutInfo (line 58) | public CutInfo(String path, boolean isCut) {
    method getPath (line 63) | public String getPath() {
    method setPath (line 67) | public void setPath(String path) {
    method getCutPath (line 71) | public String getCutPath() {
    method setCutPath (line 75) | public void setCutPath(String cutPath) {
    method getOffsetX (line 79) | public int getOffsetX() {
    method setOffsetX (line 83) | public void setOffsetX(int offsetX) {
    method getOffsetY (line 87) | public int getOffsetY() {
    method setOffsetY (line 91) | public void setOffsetY(int offsetY) {
    method getImageWidth (line 95) | public int getImageWidth() {
    method setImageWidth (line 99) | public void setImageWidth(int imageWidth) {
    method getImageHeight (line 103) | public int getImageHeight() {
    method setImageHeight (line 107) | public void setImageHeight(int imageHeight) {
    method getResultAspectRatio (line 111) | public float getResultAspectRatio() {
    method setResultAspectRatio (line 115) | public void setResultAspectRatio(float resultAspectRatio) {
    method getMimeType (line 119) | public String getMimeType() {
    method setMimeType (line 123) | public void setMimeType(String mimeType) {
    method isCut (line 127) | public boolean isCut() {
    method setCut (line 131) | public void setCut(boolean cut) {
    method getAndroidQToPath (line 135) | public String getAndroidQToPath() {
    method setAndroidQToPath (line 139) | public void setAndroidQToPath(String androidQToPath) {
    method getId (line 143) | public long getId() {
    method setId (line 147) | public void setId(long id) {

FILE: matisse/src/main/java/com/matisse/ucrop/model/ExifInfo.java
  class ExifInfo (line 6) | public class ExifInfo {
    method ExifInfo (line 12) | public ExifInfo(int exifOrientation, int exifDegrees, int exifTranslat...
    method getExifOrientation (line 18) | public int getExifOrientation() {
    method getExifDegrees (line 22) | public int getExifDegrees() {
    method getExifTranslation (line 26) | public int getExifTranslation() {
    method setExifOrientation (line 30) | public void setExifOrientation(int exifOrientation) {
    method setExifDegrees (line 34) | public void setExifDegrees(int exifDegrees) {
    method setExifTranslation (line 38) | public void setExifTranslation(int exifTranslation) {
    method equals (line 42) | @Override
    method hashCode (line 55) | @Override

FILE: matisse/src/main/java/com/matisse/ucrop/model/ImageState.java
  class ImageState (line 8) | public class ImageState {
    method ImageState (line 15) | public ImageState(RectF cropRect, RectF currentImageRect, float curren...
    method getCropRect (line 22) | public RectF getCropRect() {
    method getCurrentImageRect (line 26) | public RectF getCurrentImageRect() {
    method getCurrentScale (line 30) | public float getCurrentScale() {
    method getCurrentAngle (line 34) | public float getCurrentAngle() {

FILE: matisse/src/main/java/com/matisse/ucrop/task/BitmapCropTask.java
  class BitmapCropTask (line 39) | public class BitmapCropTask extends AsyncTask<Void, Void, Throwable> {
    method BitmapCropTask (line 64) | public BitmapCropTask(@NonNull Context context, @Nullable Bitmap viewB...
    method doInBackground (line 90) | @Override
    method crop (line 113) | private boolean crop() throws IOException {
    method saveImage (line 197) | private void saveImage(@NonNull Bitmap croppedBitmap) throws FileNotFo...
    method shouldCrop (line 221) | private boolean shouldCrop(int width, int height) {
    method onPostExecute (line 231) | @Override

FILE: matisse/src/main/java/com/matisse/ucrop/task/BitmapLoadShowTask.java
  class BitmapLoadShowTask (line 27) | public class BitmapLoadShowTask extends AsyncTask<Void, Void, BitmapLoad...
    class BitmapWorkerResult (line 38) | public static class BitmapWorkerResult {
      method BitmapWorkerResult (line 44) | public BitmapWorkerResult(@NonNull Bitmap bitmapResult, @NonNull Exi...
      method BitmapWorkerResult (line 49) | public BitmapWorkerResult(@NonNull Exception bitmapWorkerException) {
    method BitmapLoadShowTask (line 55) | public BitmapLoadShowTask(@NonNull Context context,
    method doInBackground (line 66) | @Override
    method onPostExecute (line 139) | @Override

FILE: matisse/src/main/java/com/matisse/ucrop/task/BitmapLoadTask.java
  class BitmapLoadTask (line 43) | public class BitmapLoadTask extends AsyncTask<Void, Void, BitmapLoadTask...
    class BitmapWorkerResult (line 55) | public static class BitmapWorkerResult {
      method BitmapWorkerResult (line 61) | public BitmapWorkerResult(@NonNull Bitmap bitmapResult, @NonNull Exi...
      method BitmapWorkerResult (line 66) | public BitmapWorkerResult(@NonNull Exception bitmapWorkerException) {
    method BitmapLoadTask (line 72) | public BitmapLoadTask(@NonNull Context context,
    method doInBackground (line 84) | @Override
    method processInputUri (line 162) | private void processInputUri() throws NullPointerException, IOException {
    method getFilePath (line 188) | private String getFilePath() {
    method copyFile (line 197) | private void copyFile(@NonNull Uri inputUri, @Nullable Uri outputUri) ...
    method downloadFile (line 228) | private void downloadFile(@NonNull Uri inputUri, @Nullable Uri outputU...
    method onPostExecute (line 298) | @Override

FILE: matisse/src/main/java/com/matisse/ucrop/util/BitmapLoadUtils.java
  class BitmapLoadUtils (line 31) | public class BitmapLoadUtils {
    method decodeBitmapInBackground (line 35) | public static void decodeBitmapInBackground(@NonNull Context context,
    method decodeBitmapInBackground (line 43) | public static void decodeBitmapInBackground(@NonNull Context context,
    method transformBitmap (line 52) | public static Bitmap transformBitmap(@NonNull Bitmap bitmap, @NonNull ...
    method calculateInSampleSize (line 64) | public static int calculateInSampleSize(@NonNull BitmapFactory.Options...
    method getExifOrientation (line 80) | public static int getExifOrientation(@NonNull Context context, @NonNul...
    method exifToDegrees (line 95) | public static int exifToDegrees(int exifOrientation) {
    method exifToTranslation (line 116) | public static int exifToTranslation(int exifOrientation) {
    method calculateMaxBitmapSize (line 138) | @SuppressWarnings({"SuspiciousNameCombination", "deprecation"})
    method close (line 174) | @SuppressWarnings("ConstantConditions")

FILE: matisse/src/main/java/com/matisse/ucrop/util/CubicEasing.java
  class CubicEasing (line 3) | public final class CubicEasing {
    method easeOut (line 5) | public static float easeOut(float time, float start, float end, float ...
    method easeIn (line 9) | public static float easeIn(float time, float start, float end, float d...
    method easeInOut (line 13) | public static float easeInOut(float time, float start, float end, floa...

FILE: matisse/src/main/java/com/matisse/ucrop/util/EglUtils.java
  class EglUtils (line 19) | public class EglUtils {
    method EglUtils (line 23) | private EglUtils() {
    method getMaxTextureSize (line 27) | public static int getMaxTextureSize() {
    method getMaxTextureEgl14 (line 40) | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    method getMaxTextureEgl10 (line 89) | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)

FILE: matisse/src/main/java/com/matisse/ucrop/util/FastBitmapDrawable.java
  class FastBitmapDrawable (line 25) | public class FastBitmapDrawable extends Drawable {
    method FastBitmapDrawable (line 33) | public FastBitmapDrawable(Bitmap b) {
    method draw (line 38) | @Override
    method setColorFilter (line 45) | @Override
    method getOpacity (line 50) | @Override
    method setFilterBitmap (line 55) | public void setFilterBitmap(boolean filterBitmap) {
    method getAlpha (line 59) | public int getAlpha() {
    method setAlpha (line 63) | @Override
    method getIntrinsicWidth (line 69) | @Override
    method getIntrinsicHeight (line 74) | @Override
    method getMinimumWidth (line 79) | @Override
    method getMinimumHeight (line 84) | @Override
    method getBitmap (line 89) | public Bitmap getBitmap() {
    method setBitmap (line 93) | public void setBitmap(Bitmap b) {

FILE: matisse/src/main/java/com/matisse/ucrop/util/FileUtils.java
  class FileUtils (line 48) | public class FileUtils {
    method FileUtils (line 55) | private FileUtils() {
    method isExternalStorageDocument (line 63) | public static boolean isExternalStorageDocument(Uri uri) {
    method isDownloadsDocument (line 72) | public static boolean isDownloadsDocument(Uri uri) {
    method isMediaDocument (line 81) | public static boolean isMediaDocument(Uri uri) {
    method isGooglePhotosUri (line 89) | public static boolean isGooglePhotosUri(Uri uri) {
    method getDataColumn (line 104) | public static String getDataColumn(Context context, Uri uri, String se...
    method getPath (line 142) | @SuppressLint("NewApi")
    method copyFile (line 216) | public static void copyFile(@NonNull String pathFrom, @NonNull String ...
    method isGifForSuffix (line 235) | public static boolean isGifForSuffix(String suffix) {
    method isGif (line 245) | public static boolean isGif(String mimeType) {
    method isHttp (line 255) | public static boolean isHttp(String path) {
    method copyFile (line 272) | public static boolean copyFile(FileInputStream fileInputStream, String...
    method extSuffix (line 293) | public static String extSuffix(InputStream input) {
    method getCreateFileName (line 310) | public static String getCreateFileName(String prefix) {

FILE: matisse/src/main/java/com/matisse/ucrop/util/ImageHeaderParser.java
  class ImageHeaderParser (line 46) | public class ImageHeaderParser {
    method ImageHeaderParser (line 72) | public ImageHeaderParser(InputStream is) {
    method getOrientation (line 84) | public int getOrientation() throws IOException {
    method parseExifSegment (line 106) | private int parseExifSegment(byte[] tempArray, int exifSegmentLength) ...
    method hasJpegExifPreamble (line 128) | private boolean hasJpegExifPreamble(byte[] exifData, int exifSegmentLe...
    method moveToExifSegmentAndGetLength (line 146) | private int moveToExifSegmentAndGetLength() throws IOException {
    method parseExifSegment (line 189) | private static int parseExifSegment(RandomAccessReader segmentData) {
    method calcTagOffset (line 276) | private static int calcTagOffset(int ifdOffset, int tagIndex) {
    method handles (line 280) | private static boolean handles(int imageMagicNumber) {
    class RandomAccessReader (line 286) | private static class RandomAccessReader {
      method RandomAccessReader (line 289) | public RandomAccessReader(byte[] data, int length) {
      method order (line 295) | public void order(ByteOrder byteOrder) {
      method length (line 299) | public int length() {
      method getInt32 (line 303) | public int getInt32(int offset) {
      method getInt16 (line 307) | public short getInt16(int offset) {
    type Reader (line 312) | private interface Reader {
      method getUInt16 (line 313) | int getUInt16() throws IOException;
      method getUInt8 (line 315) | short getUInt8() throws IOException;
      method skip (line 317) | long skip(long total) throws IOException;
      method read (line 319) | int read(byte[] buffer, int byteCount) throws IOException;
    class StreamReader (line 322) | private static class StreamReader implements Reader {
      method StreamReader (line 326) | public StreamReader(InputStream is) {
      method getUInt16 (line 330) | @Override
      method getUInt8 (line 335) | @Override
      method skip (line 340) | @Override
      method read (line 367) | @Override
    method copyExif (line 378) | public static void copyExif(ExifInterface originalExif, int width, int...

FILE: matisse/src/main/java/com/matisse/ucrop/util/RectUtils.java
  class RectUtils (line 5) | public class RectUtils {
    method getCornersFromRect (line 20) | public static float[] getCornersFromRect(RectF r) {
    method getRectSidesFromCorners (line 41) | public static float[] getRectSidesFromCorners(float[] corners) {
    method getCenterFromRect (line 46) | public static float[] getCenterFromRect(RectF r) {
    method trapToRect (line 57) | public static RectF trapToRect(float[] array) {

FILE: matisse/src/main/java/com/matisse/ucrop/util/RotationGestureDetector.java
  class RotationGestureDetector (line 7) | public class RotationGestureDetector {
    method RotationGestureDetector (line 19) | public RotationGestureDetector(OnRotationGestureListener listener) {
    method getAngle (line 25) | public float getAngle() {
    method onTouchEvent (line 29) | public boolean onTouchEvent(@NonNull MotionEvent event) {
    method calculateAngleBetweenLines (line 80) | private float calculateAngleBetweenLines(float fx1, float fy1, float f...
    method calculateAngleDelta (line 87) | private float calculateAngleDelta(float angleFrom, float angleTo) {
    class SimpleOnRotationGestureListener (line 99) | public static class SimpleOnRotationGestureListener implements OnRotat...
      method onRotation (line 101) | @Override
    type OnRotationGestureListener (line 107) | public interface OnRotationGestureListener {
      method onRotation (line 109) | boolean onRotation(RotationGestureDetector rotationDetector);

FILE: matisse/src/main/java/com/matisse/ucrop/util/SelectedStateListDrawable.java
  class SelectedStateListDrawable (line 10) | public class SelectedStateListDrawable extends StateListDrawable {
    method SelectedStateListDrawable (line 14) | public SelectedStateListDrawable(Drawable drawable, int selectionColor) {
    method onStateChange (line 21) | @Override
    method isStateful (line 37) | @Override

FILE: matisse/src/main/java/com/matisse/ucrop/util/VersionUtils.java
  class VersionUtils (line 8) | public class VersionUtils {
    method VersionUtils (line 10) | private VersionUtils() {
    method isAndroidQ (line 14) | public static boolean isAndroidQ() {

FILE: matisse/src/main/java/com/matisse/ucrop/view/CropImageView.java
  class CropImageView (line 33) | public class CropImageView extends TransformImageView {
    method CropImageView (line 56) | public CropImageView(Context context) {
    method CropImageView (line 60) | public CropImageView(Context context, AttributeSet attrs) {
    method CropImageView (line 64) | public CropImageView(Context context, AttributeSet attrs, int defStyle) {
    method cropAndSaveImage (line 72) | public void cropAndSaveImage(@NonNull Bitmap.CompressFormat compressFo...
    method getMaxScale (line 93) | public float getMaxScale() {
    method getMinScale (line 100) | public float getMinScale() {
    method getTargetAspectRatio (line 107) | public float getTargetAspectRatio() {
    method setCropRect (line 117) | public void setCropRect(RectF cropRect) {
    method setTargetAspectRatio (line 132) | public void setTargetAspectRatio(float targetAspectRatio) {
    method getCropBoundsChangeListener (line 150) | @Nullable
    method setCropBoundsChangeListener (line 155) | public void setCropBoundsChangeListener(@Nullable CropBoundsChangeList...
    method setMaxResultImageSizeX (line 164) | public void setMaxResultImageSizeX(@IntRange(from = 10) int maxResultI...
    method setMaxResultImageSizeY (line 173) | public void setMaxResultImageSizeY(@IntRange(from = 10) int maxResultI...
    method setImageToWrapCropBoundsAnimDuration (line 182) | public void setImageToWrapCropBoundsAnimDuration(@IntRange(from = 100)...
    method setMaxScaleMultiplier (line 195) | public void setMaxScaleMultiplier(float maxScaleMultiplier) {
    method zoomOutImage (line 202) | public void zoomOutImage(float deltaScale) {
    method zoomOutImage (line 209) | public void zoomOutImage(float scale, float centerX, float centerY) {
    method zoomInImage (line 218) | public void zoomInImage(float deltaScale) {
    method zoomInImage (line 225) | public void zoomInImage(float scale, float centerX, float centerY) {
    method postScale (line 239) | public void postScale(float deltaScale, float px, float py) {
    method postRotate (line 252) | public void postRotate(float deltaAngle) {
    method cancelAllAnimations (line 259) | public void cancelAllAnimations() {
    method setImageToWrapCropBounds (line 264) | public void setImageToWrapCropBounds() {
    method setImageToWrapCropBounds (line 276) | public void setImageToWrapCropBounds(boolean animate) {
    method calculateImageIndents (line 333) | private float[] calculateImageIndents() {
    method onImageLaidOut (line 367) | @Override
    method isImageWrapCropBounds (line 407) | protected boolean isImageWrapCropBounds() {
    method isImageWrapCropBounds (line 418) | protected boolean isImageWrapCropBounds(float[] imageCorners) {
    method zoomImageToPosition (line 439) | protected void zoomImageToPosition(float scale, float centerX, float c...
    method calculateImageScaleBounds (line 451) | private void calculateImageScaleBounds() {
    method calculateImageScaleBounds (line 465) | private void calculateImageScaleBounds(float drawableWidth, float draw...
    method setupInitialImagePosition (line 480) | private void setupInitialImagePosition(float drawableWidth, float draw...
    method processStyledAttributes (line 502) | @SuppressWarnings("deprecation")
    class WrapCropBoundsRunnable (line 520) | private static class WrapCropBoundsRunnable implements Runnable {
      method WrapCropBoundsRunnable (line 531) | public WrapCropBoundsRunnable(CropImageView cropImageView,
      method run (line 551) | @Override
    class ZoomImageToPosition (line 583) | private static class ZoomImageToPosition implements Runnable {
      method ZoomImageToPosition (line 593) | public ZoomImageToPosition(CropImageView cropImageView,
      method run (line 608) | @Override

FILE: matisse/src/main/java/com/matisse/ucrop/view/GestureCropImageView.java
  class GestureCropImageView (line 14) | public class GestureCropImageView extends CropImageView {
    method GestureCropImageView (line 27) | public GestureCropImageView(Context context) {
    method GestureCropImageView (line 31) | public GestureCropImageView(Context context, AttributeSet attrs) {
    method GestureCropImageView (line 35) | public GestureCropImageView(Context context, AttributeSet attrs, int d...
    method setScaleEnabled (line 39) | public void setScaleEnabled(boolean scaleEnabled) {
    method isScaleEnabled (line 43) | public boolean isScaleEnabled() {
    method setRotateEnabled (line 47) | public void setRotateEnabled(boolean rotateEnabled) {
    method isRotateEnabled (line 51) | public boolean isRotateEnabled() {
    method setDoubleTapScaleSteps (line 55) | public void setDoubleTapScaleSteps(int doubleTapScaleSteps) {
    method getDoubleTapScaleSteps (line 59) | public int getDoubleTapScaleSteps() {
    method onTouchEvent (line 69) | @Override
    method init (line 96) | @Override
    method getDoubleTapTargetScale (line 107) | protected float getDoubleTapTargetScale() {
    method setupGestureListeners (line 111) | private void setupGestureListeners() {
    class ScaleListener (line 117) | private class ScaleListener extends ScaleGestureDetector.SimpleOnScale...
      method onScale (line 119) | @Override
    class GestureListener (line 126) | private class GestureListener extends GestureDetector.SimpleOnGestureL...
      method onDoubleTap (line 128) | @Override
      method onScroll (line 134) | @Override
    class RotateListener (line 142) | private class RotateListener extends RotationGestureDetector.SimpleOnR...
      method onRotation (line 144) | @Override

FILE: matisse/src/main/java/com/matisse/ucrop/view/OverlayView.java
  class OverlayView (line 30) | public class OverlayView extends View {
    method OverlayView (line 75) | public OverlayView(Context context) {
    method OverlayView (line 79) | public OverlayView(Context context, AttributeSet attrs) {
    method OverlayView (line 83) | public OverlayView(Context context, AttributeSet attrs, int defStyle) {
    method getOverlayViewChangeListener (line 88) | public OverlayViewChangeListener getOverlayViewChangeListener() {
    method setOverlayViewChangeListener (line 92) | public void setOverlayViewChangeListener(OverlayViewChangeListener cal...
    method getCropViewRect (line 96) | @NonNull
    method isFreestyleCropEnabled (line 101) | public boolean isFreestyleCropEnabled() {
    method setFreestyleCropEnabled (line 105) | public void setFreestyleCropEnabled(boolean freestyleCropEnabled) {
    method ismIsDragFrame (line 109) | public boolean ismIsDragFrame() {
    method setDragFrame (line 113) | public void setDragFrame(boolean mIsDragFrame) {
    method setCircleDimmedLayer (line 122) | public void setCircleDimmedLayer(boolean circleDimmedLayer) {
    method setCropGridRowCount (line 130) | public void setCropGridRowCount(@IntRange(from = 0) int cropGridRowCou...
    method setCropGridColumnCount (line 139) | public void setCropGridColumnCount(@IntRange(from = 0) int cropGridCol...
    method setShowCropFrame (line 149) | public void setShowCropFrame(boolean showCropFrame) {
    method setShowCropGrid (line 158) | public void setShowCropGrid(boolean showCropGrid) {
    method setDimmedColor (line 167) | public void setDimmedColor(@ColorInt int dimmedColor) {
    method setCropFrameStrokeWidth (line 174) | public void setCropFrameStrokeWidth(@IntRange(from = 0) int width) {
    method setCropGridStrokeWidth (line 181) | public void setCropGridStrokeWidth(@IntRange(from = 0) int width) {
    method setCropFrameColor (line 188) | public void setCropFrameColor(@ColorInt int color) {
    method setCropGridColor (line 195) | public void setCropGridColor(@ColorInt int color) {
    method setTargetAspectRatio (line 204) | public void setTargetAspectRatio(final float targetAspectRatio) {
    method setupCropBounds (line 218) | public void setupCropBounds() {
    method updateGridPoints (line 238) | private void updateGridPoints() {
    method init (line 248) | protected void init() {
    method onLayout (line 255) | @Override
    method onDraw (line 276) | @Override
    method onTouchEvent (line 283) | @Override
    method updateCropViewRect (line 335) | private void updateCropViewRect(float touchX, float touchY) {
    method getCurrentTouchIndex (line 400) | private int getCurrentTouchIndex(float touchX, float touchY) {
    method drawDimmedLayer (line 438) | protected void drawDimmedLayer(@NonNull Canvas canvas) {
    method drawCropGrid (line 460) | protected void drawCropGrid(@NonNull Canvas canvas) {
    method processStyledAttributes (line 512) | @SuppressWarnings("deprecation")
    method initCropFrameStyle (line 531) | @SuppressWarnings("deprecation")
    method initCropGridStyle (line 549) | @SuppressWarnings("deprecation")

FILE: matisse/src/main/java/com/matisse/ucrop/view/TransformImageView.java
  class TransformImageView (line 29) | public class TransformImageView extends ImageView {
    type TransformImageListener (line 62) | public interface TransformImageListener {
      method onLoadComplete (line 64) | void onLoadComplete();
      method onLoadFailure (line 66) | void onLoadFailure(@NonNull Exception e);
      method onRotate (line 68) | void onRotate(float currentAngle);
      method onScale (line 70) | void onScale(float currentScale);
    method TransformImageView (line 74) | public TransformImageView(Context context) {
    method TransformImageView (line 78) | public TransformImageView(Context context, AttributeSet attrs) {
    method TransformImageView (line 82) | public TransformImageView(Context context, AttributeSet attrs, int def...
    method setTransformImageListener (line 87) | public void setTransformImageListener(TransformImageListener transform...
    method setScaleType (line 91) | @Override
    method setMaxBitmapSize (line 106) | public void setMaxBitmapSize(int maxBitmapSize) {
    method getMaxBitmapSize (line 110) | public int getMaxBitmapSize() {
    method setImageBitmap (line 117) | @Override
    method getImageInputUri (line 122) | public Uri getImageInputUri() {
    method getImageOutputPath (line 126) | public String getImageOutputPath() {
    method getExifInfo (line 130) | public ExifInfo getExifInfo() {
    method setImageUri (line 140) | public void setImageUri(@NonNull Uri imageUri, @Nullable Uri outputUri...
    method getCurrentScale (line 170) | public float getCurrentScale() {
    method getMatrixScale (line 177) | public float getMatrixScale(@NonNull Matrix matrix) {
    method getCurrentAngle (line 185) | public float getCurrentAngle() {
    method getMatrixAngle (line 192) | public float getMatrixAngle(@NonNull Matrix matrix) {
    method setImageMatrix (line 197) | @Override
    method getViewBitmap (line 204) | @Nullable
    method postTranslate (line 219) | public void postTranslate(float deltaX, float deltaY) {
    method postScale (line 233) | public void postScale(float deltaScale, float px, float py) {
    method postRotate (line 250) | public void postRotate(float deltaAngle, float px, float py) {
    method init (line 260) | protected void init() {
    method onLayout (line 264) | @Override
    method onImageLaidOut (line 284) | protected void onImageLaidOut() {
    method getMatrixValue (line 313) | protected float getMatrixValue(@NonNull Matrix matrix, @IntRange(from ...
    method printMatrix (line 322) | @SuppressWarnings("unused")
    method updateCurrentImagePoints (line 336) | private void updateCurrentImagePoints() {

FILE: matisse/src/main/java/com/matisse/ucrop/view/UCropView.java
  class UCropView (line 13) | public class UCropView extends FrameLayout {
    method UCropView (line 18) | public UCropView(Context context, AttributeSet attrs) {
    method UCropView (line 22) | public UCropView(Context context, AttributeSet attrs, int defStyleAttr) {
    method shouldDelayChildPressedState (line 39) | @Override
    method getCropImageView (line 44) | @NonNull
    method getOverlayView (line 49) | @NonNull

FILE: matisse/src/main/java/com/matisse/ucrop/view/widget/AspectRatioTextView.java
  class AspectRatioTextView (line 29) | public class AspectRatioTextView extends TextView {
    method AspectRatioTextView (line 39) | public AspectRatioTextView(Context context) {
    method AspectRatioTextView (line 43) | public AspectRatioTextView(Context context, AttributeSet attrs) {
    method AspectRatioTextView (line 47) | public AspectRatioTextView(Context context, AttributeSet attrs, int de...
    method AspectRatioTextView (line 53) | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    method setActiveColor (line 64) | public void setActiveColor(@ColorInt int activeColor) {
    method setAspectRatio (line 69) | public void setAspectRatio(@NonNull AspectRatio aspectRatio) {
    method getAspectRatio (line 83) | public float getAspectRatio(boolean toggleRatio) {
    method onDraw (line 91) | @Override
    method init (line 102) | @SuppressWarnings("deprecation")
    method applyActiveColor (line 128) | private void applyActiveColor(@ColorInt int activeColor) {
    method toggleAspectRatio (line 146) | private void toggleAspectRatio() {
    method setTitle (line 156) | private void setTitle() {

FILE: matisse/src/main/java/com/matisse/ucrop/view/widget/HorizontalProgressWheelView.java
  class HorizontalProgressWheelView (line 21) | public class HorizontalProgressWheelView extends View {
    method HorizontalProgressWheelView (line 37) | public HorizontalProgressWheelView(Context context) {
    method HorizontalProgressWheelView (line 41) | public HorizontalProgressWheelView(Context context, AttributeSet attrs) {
    method HorizontalProgressWheelView (line 45) | public HorizontalProgressWheelView(Context context, AttributeSet attrs...
    method HorizontalProgressWheelView (line 50) | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    method setScrollingListener (line 55) | public void setScrollingListener(ScrollingListener scrollingListener) {
    method setMiddleLineColor (line 59) | public void setMiddleLineColor(@ColorInt int middleLineColor) {
    method onTouchEvent (line 64) | @Override
    method onDraw (line 92) | @Override
    method onScrollEvent (line 121) | private void onScrollEvent(MotionEvent event, float distance) {
    method init (line 130) | private void init() {
    type ScrollingListener (line 143) | public interface ScrollingListener {
      method onScrollStart (line 145) | void onScrollStart();
      method onScroll (line 147) | void onScroll(float delta, float totalDistance);
      method onScrollEnd (line 149) | void onScrollEnd();

FILE: matisse/src/main/java/com/matisse/widget/longimage/CompatDecoderFactory.java
  class CompatDecoderFactory (line 10) | public class CompatDecoderFactory <T> implements DecoderFactory<T> {
    method CompatDecoderFactory (line 13) | public CompatDecoderFactory(@NonNull Class<? extends T> clazz) {
    method make (line 17) | @Override

FILE: matisse/src/main/java/com/matisse/widget/longimage/DecoderFactory.java
  type DecoderFactory (line 7) | public interface DecoderFactory<T> {
    method make (line 12) | T make() throws IllegalAccessException, InstantiationException;

FILE: matisse/src/main/java/com/matisse/widget/longimage/ImageDecoder.java
  type ImageDecoder (line 11) | public interface ImageDecoder {
    method decode (line 24) | Bitmap decode(Context context, Uri uri) throws Exception;

FILE: matisse/src/main/java/com/matisse/widget/longimage/ImageRegionDecoder.java
  type ImageRegionDecoder (line 13) | public interface ImageRegionDecoder {
    method init (line 26) | Point init(Context context, Uri uri) throws Exception;
    method decodeRegion (line 37) | Bitmap decodeRegion(Rect sRect, int sampleSize);
    method isReady (line 43) | boolean isReady();
    method recycle (line 48) | void recycle();

FILE: matisse/src/main/java/com/matisse/widget/longimage/ImageSource.java
  class ImageSource (line 18) | public final class ImageSource {
    method ImageSource (line 32) | private ImageSource(Bitmap bitmap, boolean cached) {
    method ImageSource (line 42) | private ImageSource(Uri uri) {
    method ImageSource (line 61) | private ImageSource(int resource) {
    method resource (line 72) | public static ImageSource resource(int resId) {
    method asset (line 80) | public static ImageSource asset(String assetName) {
    method uri (line 92) | public static ImageSource uri(String uri) {
    method uri (line 109) | public static ImageSource uri(Uri uri) {
    method bitmap (line 120) | public static ImageSource bitmap(Bitmap bitmap) {
    method cachedBitmap (line 133) | public static ImageSource cachedBitmap(Bitmap bitmap) {
    method tilingEnabled (line 145) | public ImageSource tilingEnabled() {
    method tilingDisabled (line 154) | public ImageSource tilingDisabled() {
    method tiling (line 163) | public ImageSource tiling(boolean tile) {
    method region (line 173) | public ImageSource region(Rect sRegion) {
    method dimensions (line 185) | public ImageSource dimensions(int sWidth, int sHeight) {
    method setInvariants (line 194) | private void setInvariants() {
    method getUri (line 202) | protected final Uri getUri() {
    method getBitmap (line 206) | protected final Bitmap getBitmap() {
    method getResource (line 210) | protected final Integer getResource() {
    method getTile (line 214) | protected final boolean getTile() {
    method getSWidth (line 218) | protected final int getSWidth() {
    method getSHeight (line 222) | protected final int getSHeight() {
    method getSRegion (line 226) | protected final Rect getSRegion() {
    method isCached (line 230) | protected final boolean isCached() {

FILE: matisse/src/main/java/com/matisse/widget/longimage/ImageViewState.java
  class ImageViewState (line 26) | public class ImageViewState implements Serializable {
    method ImageViewState (line 36) | public ImageViewState(float scale, PointF center, int orientation) {
    method getScale (line 43) | public float getScale() {
    method getCenter (line 47) | public PointF getCenter() {
    method getOrientation (line 51) | public int getOrientation() {

FILE: matisse/src/main/java/com/matisse/widget/longimage/SkiaImageDecoder.java
  class SkiaImageDecoder (line 21) | public class SkiaImageDecoder implements ImageDecoder {
    method decode (line 27) | @Override

FILE: matisse/src/main/java/com/matisse/widget/longimage/SkiaImageRegionDecoder.java
  class SkiaImageRegionDecoder (line 26) | public class SkiaImageRegionDecoder implements ImageRegionDecoder {
    method init (line 35) | @Override
    method decodeRegion (line 82) | @Override
    method isReady (line 96) | @Override
    method recycle (line 101) | @Override

FILE: matisse/src/main/java/com/matisse/widget/longimage/SubsamplingScaleImageView.java
  class SubsamplingScaleImageView (line 75) | @SuppressWarnings("unused")
    method SubsamplingScaleImageView (line 281) | public SubsamplingScaleImageView(Context context, AttributeSet attr) {
    method SubsamplingScaleImageView (line 331) | public SubsamplingScaleImageView(Context context) {
    method setOrientation (line 339) | public final void setOrientation(int orientation) {
    method setImage (line 353) | public final void setImage(ImageSource imageSource) {
    method setImage (line 364) | public final void setImage(ImageSource imageSource, ImageViewState sta...
    method setImage (line 378) | public final void setImage(ImageSource imageSource, ImageSource previe...
    method setImage (line 395) | public final void setImage(ImageSource imageSource, ImageSource previe...
    method reset (line 451) | private void reset(boolean newImage) {
    method setGestureDetector (line 517) | private void setGestureDetector(final Context context) {
    method onSizeChanged (line 573) | @Override
    method onMeasure (line 588) | @Override
    method onTouchEvent (line 616) | @Override
    method onTouchEventInternal (line 658) | @SuppressWarnings("deprecation")
    method requestDisallowInterceptTouchEvent (line 887) | private void requestDisallowInterceptTouchEvent(boolean disallowInterc...
    method doubleTapZoom (line 898) | private void doubleTapZoom(PointF sCenter, PointF vFocus) {
    method onDraw (line 927) | @Override
    method setMatrixArray (line 1111) | private void setMatrixArray(float[] array, float f0, float f1, float f...
    method isBaseLayerReady (line 1125) | private boolean isBaseLayerReady() {
    method checkReady (line 1149) | private boolean checkReady() {
    method checkImageLoaded (line 1166) | private boolean checkImageLoaded() {
    method createPaints (line 1182) | private void createPaints() {
    method initialiseBaseLayer (line 1201) | private synchronized void initialiseBaseLayer(Point maxTileDimensions) {
    method refreshRequiredTiles (line 1243) | private void refreshRequiredTiles(boolean load) {
    method tileVisible (line 1284) | private boolean tileVisible(Tile tile) {
    method preDraw (line 1295) | private void preDraw() {
    method calculateInSampleSize (line 1321) | private int calculateInSampleSize(float scale) {
    method fitToBounds (line 1365) | private void fitToBounds(boolean center, ScaleAndTranslate sat) {
    method fitToBounds (line 1414) | private void fitToBounds(boolean center) {
    method initialiseTileMap (line 1436) | private void initialiseTileMap(Point maxTileDimensions) {
    class TilesInitTask (line 1486) | private static class TilesInitTask extends AsyncTask<Void, Void, int[]> {
      method TilesInitTask (line 1494) | TilesInitTask(SubsamplingScaleImageView view, Context context, Decod...
      method doInBackground (line 1501) | @Override
      method onPostExecute (line 1528) | @Override
    method onTilesInited (line 1544) | private synchronized void onTilesInited(ImageRegionDecoder decoder, in...
    class TileLoadTask (line 1576) | private static class TileLoadTask extends AsyncTask<Void, Void, Bitmap> {
      method TileLoadTask (line 1582) | TileLoadTask(SubsamplingScaleImageView view, ImageRegionDecoder deco...
      method doInBackground (line 1589) | @Override
      method onPostExecute (line 1618) | @Override
    method onTileLoaded (line 1637) | private synchronized void onTileLoaded() {
    class BitmapLoadTask (line 1658) | private static class BitmapLoadTask extends AsyncTask<Void, Void, Inte...
      method BitmapLoadTask (line 1667) | BitmapLoadTask(SubsamplingScaleImageView view, Context context, Deco...
      method doInBackground (line 1675) | @Override
      method onPostExecute (line 1697) | @Override
    method onPreviewLoaded (line 1721) | private synchronized void onPreviewLoaded(Bitmap previewBitmap) {
    method onImageLoaded (line 1742) | private synchronized void onImageLoaded(Bitmap bitmap, int sOrientatio...
    method getExifOrientation (line 1774) | @AnyThread
    method execute (line 1821) | private void execute(AsyncTask<Void, Void, ?> asyncTask) {
    class Tile (line 1836) | private static class Tile {
    class Anim (line 1850) | private static class Anim {
    class ScaleAndTranslate (line 1868) | private static class ScaleAndTranslate {
      method ScaleAndTranslate (line 1869) | private ScaleAndTranslate(float scale, PointF vTranslate) {
    method restoreState (line 1880) | private void restoreState(ImageViewState state) {
    method setMaxTileSize (line 1894) | public void setMaxTileSize(int maxPixels) {
    method setMaxTileSize (line 1905) | public void setMaxTileSize(int maxPixelsX, int maxPixelsY) {
    method getMaxBitmapDimensions (line 1913) | private Point getMaxBitmapDimensions(Canvas canvas) {
    method sWidth (line 1930) | @SuppressWarnings("SuspiciousNameCombination")
    method sHeight (line 1943) | @SuppressWarnings("SuspiciousNameCombination")
    method fileSRect (line 1957) | @SuppressWarnings("SuspiciousNameCombination")
    method getRequiredRotation (line 1974) | @AnyThread
    method distance (line 1986) | private float distance(float x0, float x1, float y0, float y1) {
    method recycle (line 1997) | public void recycle() {
    method viewToSourceX (line 2007) | private float viewToSourceX(float vx) {
    method viewToSourceY (line 2015) | private float viewToSourceY(float vy) {
    method viewToSourceCoord (line 2023) | public final PointF viewToSourceCoord(PointF vxy) {
    method viewToSourceCoord (line 2030) | public final PointF viewToSourceCoord(float vx, float vy) {
    method viewToSourceCoord (line 2037) | public final PointF viewToSourceCoord(PointF vxy, PointF sTarget) {
    method viewToSourceCoord (line 2044) | public final PointF viewToSourceCoord(float vx, float vy, PointF sTarg...
    method sourceToViewX (line 2055) | private float sourceToViewX(float sx) {
    method sourceToViewY (line 2063) | private float sourceToViewY(float sy) {
    method sourceToViewCoord (line 2071) | public final PointF sourceToViewCoord(PointF sxy) {
    method sourceToViewCoord (line 2078) | public final PointF sourceToViewCoord(float sx, float sy) {
    method sourceToViewCoord (line 2085) | public final PointF sourceToViewCoord(PointF sxy, PointF vTarget) {
    method sourceToViewCoord (line 2092) | public final PointF sourceToViewCoord(float sx, float sy, PointF vTarg...
    method sourceToViewRect (line 2103) | private Rect sourceToViewRect(Rect sRect, Rect vTarget) {
    method vTranslateForSCenter (line 2118) | private PointF vTranslateForSCenter(float sCenterX, float sCenterY, fl...
    method limitedSCenter (line 2134) | private PointF limitedSCenter(float sCenterX, float sCenterY, float sc...
    method minScale (line 2147) | private float minScale() {
    method limitedScale (line 2162) | private float limitedScale(float targetScale) {
    method ease (line 2177) | private float ease(int type, long time, float from, float change, long...
    method easeOutQuad (line 2196) | private float easeOutQuad(long time, float from, float change, long du...
    method easeInOutQuad (line 2209) | private float easeInOutQuad(long time, float from, float change, long ...
    method debug (line 2222) | @AnyThread
    method setRegionDecoderClass (line 2236) | public final void setRegionDecoderClass(Class<? extends ImageRegionDec...
    method setRegionDecoderFactory (line 2249) | public final void setRegionDecoderFactory(DecoderFactory<? extends Ima...
    method setBitmapDecoderClass (line 2262) | public final void setBitmapDecoderClass(Class<? extends ImageDecoder> ...
    method setBitmapDecoderFactory (line 2274) | public final void setBitmapDecoderFactory(DecoderFactory<? extends Ima...
    method setPanLimit (line 2284) | public final void setPanLimit(int panLimit) {
    method setMinimumScaleType (line 2298) | public final void setMinimumScaleType(int scaleType) {
    method setMaxScale (line 2314) | public final void setMaxScale(float maxScale) {
    method setMinScale (line 2322) | public final void setMinScale(float minScale) {
    method setMinimumDpi (line 2332) | public final void setMinimumDpi(int dpi) {
    method setMaximumDpi (line 2343) | public final void setMaximumDpi(int dpi) {
    method getMaxScale (line 2352) | public float getMaxScale() {
    method getMinScale (line 2359) | public final float getMinScale() {
    method setMinimumTileDpi (line 2371) | public void setMinimumTileDpi(int minimumTileDpi) {
    method getCenter (line 2384) | public final PointF getCenter() {
    method getScale (line 2393) | public final float getScale() {
    method setScaleAndCenter (line 2403) | public final void setScaleAndCenter(float scale, PointF sCenter) {
    method resetScaleAndCenter (line 2415) | public final void resetScaleAndCenter() {
    method isReady (line 2432) | public final boolean isReady() {
    method onReady (line 2441) | protected void onReady() {
    method isImageLoaded (line 2449) | public final boolean isImageLoaded() {
    method onImageLoaded (line 2456) | protected void onImageLoaded() {
    method getSWidth (line 2464) | public final int getSWidth() {
    method getSHeight (line 2472) | public final int getSHeight() {
    method getOrientation (line 2480) | public final int getOrientation() {
    method getAppliedOrientation (line 2488) | public final int getAppliedOrientation() {
    method getState (line 2496) | public final ImageViewState getState() {
    method isZoomEnabled (line 2506) | public final boolean isZoomEnabled() {
    method setZoomEnabled (line 2513) | public final void setZoomEnabled(boolean zoomEnabled) {
    method isQuickScaleEnabled (line 2520) | public final boolean isQuickScaleEnabled() {
    method setQuickScaleEnabled (line 2527) | public final void setQuickScaleEnabled(boolean quickScaleEnabled) {
    method isPanEnabled (line 2534) | public final boolean isPanEnabled() {
    method setPanEnabled (line 2541) | public final void setPanEnabled(boolean panEnabled) {
    method setTileBackgroundColor (line 2557) | public final void setTileBackgroundColor(int tileBgColor) {
    method setDoubleTapZoomScale (line 2574) | public final void setDoubleTapZoomScale(float doubleTapZoomScale) {
    method setDoubleTapZoomDpi (line 2584) | public final void setDoubleTapZoomDpi(int dpi) {
    method setDoubleTapZoomStyle (line 2594) | public final void setDoubleTapZoomStyle(int doubleTapZoomStyle) {
    method setDoubleTapZoomDuration (line 2605) | public final void setDoubleTapZoomDuration(int durationMs) {
    method setParallelLoadingEnabled (line 2616) | public void setParallelLoadingEnabled(boolean parallelLoadingEnabled) {
    method setDebug (line 2623) | public final void setDebug(boolean debug) {
    method hasImage (line 2631) | public boolean hasImage() {
    method setOnLongClickListener (line 2638) | @Override
    method setOnImageEventListener (line 2646) | public void setOnImageEventListener(OnImageEventListener onImageEventL...
    method setOnStateChangedListener (line 2653) | public void setOnStateChangedListener(OnStateChangedListener onStateCh...
    method sendStateChanged (line 2657) | private void sendStateChanged(float oldScale, PointF oldVTranslate, in...
    method animateCenter (line 2676) | public AnimationBuilder animateCenter(PointF sCenter) {
    method animateScale (line 2689) | public AnimationBuilder animateScale(float scale) {
    method animateScaleAndCenter (line 2702) | public AnimationBuilder animateScaleAndCenter(float scale, PointF sCen...
    class AnimationBuilder (line 2713) | public final class AnimationBuilder {
      method AnimationBuilder (line 2725) | private AnimationBuilder(PointF sCenter) {
      method AnimationBuilder (line 2731) | private AnimationBuilder(float scale) {
      method AnimationBuilder (line 2737) | private AnimationBuilder(float scale, PointF sCenter) {
      method AnimationBuilder (line 2743) | private AnimationBuilder(float scale, PointF sCenter, PointF vFocus) {
      method withDuration (line 2754) | public AnimationBuilder withDuration(long duration) {
      method withInterruptible (line 2764) | public AnimationBuilder withInterruptible(boolean interruptible) {
      method withEasing (line 2774) | public AnimationBuilder withEasing(int easing) {
      method withOnAnimationEventListener (line 2787) | public AnimationBuilder withOnAnimationEventListener(OnAnimationEven...
      method withPanLimited (line 2798) | private AnimationBuilder withPanLimited(boolean panLimited) {
      method withOrigin (line 2806) | private AnimationBuilder withOrigin(int origin) {
      method start (line 2814) | public void start() {
    type OnAnimationEventListener (line 2871) | public interface OnAnimationEventListener {
      method onComplete (line 2876) | void onComplete();
      method onInterruptedByUser (line 2881) | void onInterruptedByUser();
      method onInterruptedByNewAnim (line 2886) | void onInterruptedByNewAnim();
    class DefaultOnAnimationEventListener (line 2893) | public static class DefaultOnAnimationEventListener implements OnAnima...
      method onComplete (line 2895) | @Override public void onComplete() { }
      method onInterruptedByUser (line 2896) | @Override public void onInterruptedByUser() { }
      method onInterruptedByNewAnim (line 2897) | @Override public void onInterruptedByNewAnim() { }
    type OnImageEventListener (line 2904) | public interface OnImageEventListener {
      method onReady (line 2912) | void onReady();
      method onImageLoaded (line 2921) | void onImageLoaded();
      method onPreviewLoadError (line 2929) | void onPreviewLoadError(Exception e);
      method onImageLoadError (line 2938) | void onImageLoadError(Exception e);
      method onTileLoadError (line 2947) | void onTileLoadError(Exception e);
      method onPreviewReleased (line 2953) | void onPreviewReleased();
    class DefaultOnImageEventListener (line 2959) | public static class DefaultOnImageEventListener implements OnImageEven...
      method onReady (line 2961) | @Override public void onReady() { }
      method onImageLoaded (line 2962) | @Override public void onImageLoaded() { }
      method onPreviewLoadError (line 2963) | @Override public void onPreviewLoadError(Exception e) { }
      method onImageLoadError (line 2964) | @Override public void onImageLoadError(Exception e) { }
      method onTileLoadError (line 2965) | @Override public void onTileLoadError(Exception e) { }
      method onPreviewReleased (line 2966) | @Override public void onPreviewReleased() { }
    type OnStateChangedListener (line 2976) | public interface OnStateChangedListener {
      method onScaleChanged (line 2984) | void onScaleChanged(float newScale, int origin);
      method onCenterChanged (line 2991) | void onCenterChanged(PointF newCenter, int origin);
    class DefaultOnStateChangedListener (line 2998) | public static class DefaultOnStateChangedListener implements OnStateCh...
      method onCenterChanged (line 3000) | @Override public void onCenterChanged(PointF newCenter, int origin) { }
      method onScaleChanged (line 3001) | @Override public void onScaleChanged(float newScale, int origin) { }

FILE: matisse/src/test/java/com/matisse/ExampleUnitTest.java
  class ExampleUnitTest (line 12) | public class ExampleUnitTest {
    method addition_isCorrect (line 13) | @Test
Condensed preview — 197 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (869K chars).
[
  {
    "path": ".gitignore",
    "chars": 157,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n.DS_Store\n.idea\n/build\nbuild\n/c"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 15197,
    "preview": "[ ![Download](https://api.bintray.com/packages/nfleo/MatisseKotlin/MatisseKotlin/images/download.svg?version=2.1.1) ](ht"
  },
  {
    "path": "app/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "chars": 1839,
    "preview": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\n\nandroi"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "app/src/androidTest/java/com/leo/matisse/ExampleInstrumentedTest.kt",
    "chars": 632,
    "preview": "package com.leo.matisse\n\nimport android.support.test.InstrumentationRegistry\nimport android.support.test.runner.AndroidJ"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 1163,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "app/src/main/java/com/leo/matisse/ExampleActivity.kt",
    "chars": 15191,
    "preview": "package com.leo.matisse\n\nimport android.Manifest\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimpo"
  },
  {
    "path": "app/src/main/java/com/leo/matisse/FrescoEngine.kt",
    "chars": 4912,
    "preview": "package com.leo.matisse\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.net.Uri"
  },
  {
    "path": "app/src/main/java/com/leo/matisse/Glide4Engine.kt",
    "chars": 2440,
    "preview": "package com.leo.matisse\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.net.Uri"
  },
  {
    "path": "app/src/main/java/com/leo/matisse/GlideEngine.kt",
    "chars": 2449,
    "preview": "package com.leo.matisse\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.net.Uri"
  },
  {
    "path": "app/src/main/java/com/leo/matisse/ImageSizeFilter.kt",
    "chars": 919,
    "preview": "package com.leo.matisse\n\nimport android.content.Context\nimport com.matisse.MimeTypeManager\nimport com.matisse.entity.Inc"
  },
  {
    "path": "app/src/main/java/com/leo/matisse/MainActivity.kt",
    "chars": 2830,
    "preview": "package com.leo.matisse\n\nimport android.Manifest\nimport android.app.Activity\nimport android.content.Intent\nimport androi"
  },
  {
    "path": "app/src/main/java/com/leo/matisse/SizeFilter.kt",
    "chars": 834,
    "preview": "package com.leo.matisse\n\nimport android.content.Context\nimport com.matisse.MimeType\nimport com.matisse.MimeTypeManager\ni"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 5606,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:wi"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "chars": 1880,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    "
  },
  {
    "path": "app/src/main/res/drawable-xhdpi/ic_launcher_foreground.xml",
    "chars": 1880,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    "
  },
  {
    "path": "app/src/main/res/layout/activity_example.xml",
    "chars": 22700,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 1580,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 265,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"color"
  },
  {
    "path": "app/src/main/res/values/colors_dracula.xml",
    "chars": 642,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2."
  },
  {
    "path": "app/src/main/res/values/ids.xml",
    "chars": 112,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"fresco_drawee\" type=\"id\"></item>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 419,
    "preview": "<resources>\n    <string name=\"app_name\">MatisseKotlin</string>\n    <string name=\"permission_request_denied\">请求读写权限失败!</s"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 1892,
    "preview": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">"
  },
  {
    "path": "app/src/main/res/xml/file_paths_public.xml",
    "chars": 126,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n    <external-path\n        name=\"my_images\"\n        path=\"Pictures\" />\n</"
  },
  {
    "path": "app/src/test/java/com/leo/matisse/ExampleUnitTest.kt",
    "chars": 340,
    "preview": "package com.leo.matisse\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will ex"
  },
  {
    "path": "build.gradle",
    "chars": 1131,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    e"
  },
  {
    "path": "gradle.properties",
    "chars": 779,
    "preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
  },
  {
    "path": "gradlew",
    "chars": 5296,
    "preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up"
  },
  {
    "path": "gradlew.bat",
    "chars": 2176,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "matisse/build.gradle",
    "chars": 2506,
    "preview": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\n\napply plug"
  },
  {
    "path": "matisse/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "matisse/src/androidTest/java/com/matisse/ExampleInstrumentedTest.java",
    "chars": 722,
    "preview": "package com.matisse;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport androi"
  },
  {
    "path": "matisse/src/main/AndroidManifest.xml",
    "chars": 717,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.matisse\">\n\n    <uses-permission an"
  },
  {
    "path": "matisse/src/main/java/com/matisse/Matisse.kt",
    "chars": 4189,
    "preview": "package com.matisse\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.net.Uri\nimport androidx.fr"
  },
  {
    "path": "matisse/src/main/java/com/matisse/MimeType.kt",
    "chars": 2432,
    "preview": "package com.matisse\n\n/**\n * Describe : MIME Type enumeration to restrict selectable media on the selection activity.\n * "
  },
  {
    "path": "matisse/src/main/java/com/matisse/MimeTypeManager.kt",
    "chars": 3737,
    "preview": "package com.matisse\n\nimport android.content.Context\nimport android.net.Uri\nimport android.text.TextUtils\nimport android."
  },
  {
    "path": "matisse/src/main/java/com/matisse/SelectionCreator.kt",
    "chars": 11437,
    "preview": "package com.matisse\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimport and"
  },
  {
    "path": "matisse/src/main/java/com/matisse/engine/ImageEngine.kt",
    "chars": 2010,
    "preview": "package com.matisse.engine\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.net."
  },
  {
    "path": "matisse/src/main/java/com/matisse/entity/Album.kt",
    "chars": 3039,
    "preview": "package com.matisse.entity\n\nimport android.content.Context\nimport android.database.Cursor\nimport android.net.Uri\nimport "
  },
  {
    "path": "matisse/src/main/java/com/matisse/entity/CaptureStrategy.kt",
    "chars": 128,
    "preview": "package com.matisse.entity\n\ndata class CaptureStrategy(var isPublic: Boolean, var authority: String, var directory: Stri"
  },
  {
    "path": "matisse/src/main/java/com/matisse/entity/ConstValue.kt",
    "chars": 1083,
    "preview": "package com.matisse.entity\n\nimport com.matisse.ucrop.UCrop\n\nobject ConstValue {\n    const val EXTRA_RESULT_SELECTION = \""
  },
  {
    "path": "matisse/src/main/java/com/matisse/entity/IncapableCause.kt",
    "chars": 2240,
    "preview": "package com.matisse.entity\n\nimport android.content.Context\nimport android.widget.Toast\nimport androidx.annotation.IntDef"
  },
  {
    "path": "matisse/src/main/java/com/matisse/entity/Item.kt",
    "chars": 2332,
    "preview": "package com.matisse.entity\n\nimport android.content.ContentUris\nimport android.database.Cursor\nimport android.net.Uri\nimp"
  },
  {
    "path": "matisse/src/main/java/com/matisse/filter/Filter.kt",
    "chars": 1287,
    "preview": "package com.matisse.filter\n\nimport android.content.Context\nimport com.matisse.MimeType\nimport com.matisse.MimeTypeManage"
  },
  {
    "path": "matisse/src/main/java/com/matisse/internal/entity/SelectionSpec.kt",
    "chars": 3989,
    "preview": "package com.matisse.internal.entity\n\nimport android.content.Context\nimport android.content.pm.ActivityInfo\nimport androi"
  },
  {
    "path": "matisse/src/main/java/com/matisse/listener/OnCheckedListener.kt",
    "chars": 166,
    "preview": "package com.matisse.listener\n\n/**\n * Created by Lijianyou on 2018-09-07.\n * @author  Lijianyou\n */\ninterface OnCheckedLi"
  },
  {
    "path": "matisse/src/main/java/com/matisse/listener/OnSelectedListener.kt",
    "chars": 343,
    "preview": "package com.matisse.listener\n\nimport android.net.Uri\n\n/**\n * Created by Lijianyou on 2018-09-07.\n * @author  Lijianyou\n "
  },
  {
    "path": "matisse/src/main/java/com/matisse/loader/AlbumLoader.kt",
    "chars": 8274,
    "preview": "package com.matisse.loader\n\nimport android.content.ContentUris\nimport android.content.Context\nimport android.database.Cu"
  },
  {
    "path": "matisse/src/main/java/com/matisse/loader/AlbumMediaLoader.kt",
    "chars": 5781,
    "preview": "package com.matisse.loader\n\nimport android.content.Context\nimport android.database.Cursor\nimport android.database.Matrix"
  },
  {
    "path": "matisse/src/main/java/com/matisse/model/AlbumCallbacks.kt",
    "chars": 169,
    "preview": "package com.matisse.model\n\nimport android.database.Cursor\n\ninterface AlbumCallbacks {\n    fun onAlbumStart()\n    fun onA"
  },
  {
    "path": "matisse/src/main/java/com/matisse/model/AlbumCollection.kt",
    "chars": 2244,
    "preview": "package com.matisse.model\n\nimport android.content.Context\nimport android.database.Cursor\nimport android.os.Bundle\nimport"
  },
  {
    "path": "matisse/src/main/java/com/matisse/model/AlbumMediaCollection.kt",
    "chars": 2063,
    "preview": "package com.matisse.model\n\nimport android.content.Context\nimport android.database.Cursor\nimport android.os.Bundle\nimport"
  },
  {
    "path": "matisse/src/main/java/com/matisse/model/SelectedItemCollection.kt",
    "chars": 8758,
    "preview": "package com.matisse.model\n\nimport android.content.Context\nimport android.content.res.Resources\nimport android.net.Uri\nim"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/Compat.java",
    "chars": 1426,
    "preview": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/CustomGestureDetector.java",
    "chars": 7502,
    "preview": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/OnGestureListener.java",
    "chars": 1020,
    "preview": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/OnMatrixChangedListener.java",
    "chars": 514,
    "preview": "package com.matisse.photoview;\n\nimport android.graphics.RectF;\n\n/**\n * Interface definition for a callback to be invoked"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/OnOutsidePhotoTapListener.java",
    "chars": 289,
    "preview": "package com.matisse.photoview;\n\nimport android.widget.ImageView;\n\n/**\n * Callback when the user tapped outside of the ph"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/OnPhotoTapListener.java",
    "chars": 745,
    "preview": "package com.matisse.photoview;\n\nimport android.widget.ImageView;\n\n/**\n * A callback to be invoked when the Photo is tapp"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/OnScaleChangedListener.java",
    "chars": 508,
    "preview": "package com.matisse.photoview;\n\n\n/**\n * Interface definition for callback to be invoked when attached ImageView scale ch"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/OnSingleFlingListener.java",
    "chars": 685,
    "preview": "package com.matisse.photoview;\n\nimport android.view.MotionEvent;\n\n/**\n * A callback to be invoked when the ImageView is "
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/OnViewDragListener.java",
    "chars": 491,
    "preview": "package com.matisse.photoview;\n\n/**\n * Interface definition for a callback to be invoked when the photo is experiencing "
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/OnViewTapListener.java",
    "chars": 537,
    "preview": "package com.matisse.photoview;\n\nimport android.view.View;\n\npublic interface OnViewTapListener {\n\n    /**\n     * A callba"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/PhotoView.java",
    "chars": 7468,
    "preview": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/PhotoViewAttacher.java",
    "chars": 30232,
    "preview": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *"
  },
  {
    "path": "matisse/src/main/java/com/matisse/photoview/Util.java",
    "chars": 1252,
    "preview": "package com.matisse.photoview;\n\nimport android.view.MotionEvent;\nimport android.widget.ImageView;\n\nclass Util {\n\n    sta"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/PictureMultiCuttingActivity.java",
    "chars": 28713,
    "preview": "package com.matisse.ucrop;\n\nimport android.annotation.TargetApi;\nimport android.content.Intent;\nimport android.graphics."
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/PicturePhotoGalleryAdapter.java",
    "chars": 4666,
    "preview": "/*\n * Copyright (C) 2014 pengjianbo(pengjianbosoft@gmail.com), Inc.\n *\n * Licensed under the Apache License, Version 2.0"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/UCrop.java",
    "chars": 23642,
    "preview": "package com.matisse.ucrop;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\n"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/UCropActivity.java",
    "chars": 12749,
    "preview": "package com.matisse.ucrop;\n\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimpor"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/UCropMulti.java",
    "chars": 24486,
    "preview": "package com.matisse.ucrop;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\n"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/callback/Callback.kt",
    "chars": 909,
    "preview": "@file:JvmName(\"Callback\")\npackage com.matisse.ucrop.callback\n\nimport android.graphics.Bitmap\nimport android.graphics.Rec"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/immersion/CropImmersiveManage.java",
    "chars": 4355,
    "preview": "package com.matisse.ucrop.immersion;\n\nimport android.graphics.Color;\nimport android.os.Build;\nimport android.view.Window"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/immersion/CropLightStatusBarUtils.java",
    "chars": 9029,
    "preview": "package com.matisse.ucrop.immersion;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.o"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/immersion/CropRomUtils.java",
    "chars": 3997,
    "preview": "package com.matisse.ucrop.immersion;\n\nimport android.os.Build;\nimport android.text.TextUtils;\n\nimport java.io.BufferedRe"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/model/AspectRatio.java",
    "chars": 1428,
    "preview": "package com.matisse.ucrop.model;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport androidx.annotation.Nu"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/model/CropParameters.java",
    "chars": 1582,
    "preview": "package com.matisse.ucrop.model;\n\nimport android.graphics.Bitmap;\nimport android.net.Uri;\n\n/**\n * Created by Oleksii Shl"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/model/CutInfo.java",
    "chars": 2596,
    "preview": "package com.matisse.ucrop.model;\n\nimport java.io.Serializable;\n\n/**\n * @author:luck\n * @data:2017/05/30 晚上23:00\n * @描述: "
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/model/ExifInfo.java",
    "chars": 1600,
    "preview": "package com.matisse.ucrop.model;\n\n/**\n * Created by Oleksii Shliama [https://github.com/shliama] on 6/21/16.\n */\npublic "
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/model/ImageState.java",
    "chars": 848,
    "preview": "package com.matisse.ucrop.model;\n\nimport android.graphics.RectF;\n\n/**\n * Created by Oleksii Shliama [https://github.com/"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/task/BitmapCropTask.java",
    "chars": 9805,
    "preview": "package com.matisse.ucrop.task;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/task/BitmapLoadShowTask.java",
    "chars": 5462,
    "preview": "package com.matisse.ucrop.task;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/task/BitmapLoadTask.java",
    "chars": 11407,
    "preview": "package com.matisse.ucrop.task;\n\nimport android.Manifest.permission;\nimport android.content.Context;\nimport android.cont"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/BitmapLoadUtils.java",
    "chars": 6867,
    "preview": "package com.matisse.ucrop.util;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/CubicEasing.java",
    "chars": 649,
    "preview": "package com.matisse.ucrop.util;\n\npublic final class CubicEasing {\n\n    public static float easeOut(float time, float sta"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/EglUtils.java",
    "chars": 4629,
    "preview": "package com.matisse.ucrop.util;\n\nimport android.annotation.TargetApi;\nimport android.opengl.EGL14;\nimport android.opengl"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/FastBitmapDrawable.java",
    "chars": 2463,
    "preview": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/FileUtils.java",
    "chars": 10625,
    "preview": "/*\n * Copyright (C) 2007-2008 OpenIntents.org\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * y"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/ImageHeaderParser.java",
    "chars": 15960,
    "preview": "/*\n * Copyright 2015 Google, Inc. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or "
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/RectUtils.java",
    "chars": 2369,
    "preview": "package com.matisse.ucrop.util;\n\nimport android.graphics.RectF;\n\npublic class RectUtils {\n\n    /**\n     * Gets a float a"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/RotationGestureDetector.java",
    "chars": 3702,
    "preview": "package com.matisse.ucrop.util;\n\nimport android.view.MotionEvent;\n\nimport androidx.annotation.NonNull;\n\npublic class Rot"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/SelectedStateListDrawable.java",
    "chars": 1188,
    "preview": "package com.matisse.ucrop.util;\n\nimport android.graphics.PorterDuff;\nimport android.graphics.drawable.Drawable;\nimport a"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/util/VersionUtils.java",
    "chars": 297,
    "preview": "package com.matisse.ucrop.util;\n\nimport android.os.Build;\n\n/**\n * Created by Oleksii Shliama [https://github.com/shliama"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/view/CropImageView.java",
    "chars": 24525,
    "preview": "package com.matisse.ucrop.view;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.g"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/view/GestureCropImageView.java",
    "chars": 4744,
    "preview": "package com.matisse.ucrop.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.G"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/view/OverlayView.java",
    "chars": 20619,
    "preview": "package com.matisse.ucrop.view;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.g"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/view/TransformImageView.java",
    "chars": 11248,
    "preview": "package com.matisse.ucrop.view;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/view/UCropView.java",
    "chars": 1603,
    "preview": "package com.matisse.ucrop.view;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.u"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/view/widget/AspectRatioTextView.java",
    "chars": 5493,
    "preview": "package com.matisse.ucrop.view.widget;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport andr"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ucrop/view/widget/HorizontalProgressWheelView.java",
    "chars": 5575,
    "preview": "package com.matisse.ucrop.view.widget;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport andr"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/activity/AlbumPreviewActivity.kt",
    "chars": 2024,
    "preview": "package com.matisse.ui.activity\n\nimport android.database.Cursor\nimport com.matisse.entity.Album\nimport com.matisse.entit"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/activity/BaseActivity.kt",
    "chars": 2191,
    "preview": "package com.matisse.ui.activity\n\nimport android.app.Activity\nimport android.content.pm.ActivityInfo\nimport android.os.Bu"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/activity/BasePreviewActivity.kt",
    "chars": 10070,
    "preview": "package com.matisse.ui.activity\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.net.Uri\nimport"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/activity/SelectedPreviewActivity.kt",
    "chars": 1509,
    "preview": "package com.matisse.ui.activity\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Inten"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/activity/matisse/AlbumFolderSheetHelper.kt",
    "chars": 3255,
    "preview": "package com.matisse.ui.activity.matisse\n\nimport android.content.Context\nimport android.database.Cursor\nimport android.ne"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/activity/matisse/AlbumLoadHelper.kt",
    "chars": 1015,
    "preview": "package com.matisse.ui.activity.matisse\n\nimport android.os.Bundle\nimport com.matisse.model.AlbumCallbacks\nimport com.mat"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/activity/matisse/IAlbumLoad.kt",
    "chars": 213,
    "preview": "package com.matisse.ui.activity.matisse\n\nimport android.database.Cursor\n\ninterface IAlbumLoad {\n\n    /**\n     * 相册查询完成回调"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/activity/matisse/MatisseActivity.kt",
    "chars": 12263,
    "preview": "package com.matisse.ui.activity.matisse\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.databa"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/adapter/AlbumMediaAdapter.kt",
    "chars": 9133,
    "preview": "package com.matisse.ui.adapter\n\nimport android.content.Context\nimport android.database.Cursor\nimport android.graphics.dr"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/adapter/FolderItemMediaAdapter.kt",
    "chars": 3638,
    "preview": "package com.matisse.ui.adapter\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android."
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/adapter/PicturePreviewPagerAdapter.kt",
    "chars": 2155,
    "preview": "package com.matisse.ui.adapter\n\nimport android.util.SparseArray\nimport android.view.LayoutInflater\nimport android.view.V"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/adapter/PreviewPagerAdapter.kt",
    "chars": 1230,
    "preview": "package com.matisse.ui.adapter\n\nimport android.view.ViewGroup\nimport androidx.fragment.app.FragmentManager\nimport androi"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/adapter/RecyclerViewCursorAdapter.kt",
    "chars": 2240,
    "preview": "package com.matisse.ui.adapter\n\nimport android.database.Cursor\nimport android.provider.MediaStore\nimport androidx.recycl"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/view/BottomSheetDialogFragment.kt",
    "chars": 3402,
    "preview": "package com.matisse.ui.view\n\nimport android.os.Bundle\nimport android.util.DisplayMetrics\nimport android.view.*\nimport an"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/view/FolderBottomSheet.kt",
    "chars": 3048,
    "preview": "package com.matisse.ui.view\n\nimport android.content.Context\nimport android.os.Bundle\nimport android.view.LayoutInflater\n"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/view/MediaSelectionFragment.kt",
    "chars": 4318,
    "preview": "package com.matisse.ui.view\n\nimport android.content.Context\nimport android.database.Cursor\nimport android.os.Bundle\nimpo"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/view/PicturePreviewItemFragment.kt",
    "chars": 4183,
    "preview": "package com.matisse.ui.view\n\nimport android.content.Intent\nimport android.graphics.Point\nimport android.graphics.PointF\n"
  },
  {
    "path": "matisse/src/main/java/com/matisse/ui/view/PreviewItemFragment.kt",
    "chars": 2840,
    "preview": "package com.matisse.ui.view\n\nimport android.content.Intent\nimport android.graphics.Point\nimport android.os.Bundle\nimport"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/BitmapUtils.kt",
    "chars": 1464,
    "preview": "@file:JvmName(\"BitmapUtils\")\n\npackage com.matisse.utils\n\nimport android.content.Context\nimport android.graphics.Bitmap\ni"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/ExifInterfaceCompat.kt",
    "chars": 349,
    "preview": "package com.matisse.utils\n\nimport android.media.ExifInterface\n\n/**\n * Created by liubo on 2018/9/6.\n */\nobject ExifInter"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/IntentUtils.kt",
    "chars": 4635,
    "preview": "@file:JvmName(\"IntentUtils\")\n\npackage com.matisse.utils\n\nimport android.app.Activity\nimport android.content.Intent\nimpor"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/ItemSelectUtils.kt",
    "chars": 540,
    "preview": "@file:JvmName(\"ItemSelectUtils\")\n\npackage com.matisse.utils\n\nimport com.matisse.internal.entity.SelectionSpec\nimport com"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/MediaStoreCompat.kt",
    "chars": 2983,
    "preview": "package com.matisse.utils\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimpo"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/PathUtils.kt",
    "chars": 8298,
    "preview": "@file:JvmName(\"PathUtils\")\n\npackage com.matisse.utils\n\nimport android.content.ContentResolver\nimport android.content.Con"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/PhotoMetadataUtils.kt",
    "chars": 4898,
    "preview": "package com.matisse.utils\n\nimport android.app.Activity\nimport android.content.ContentResolver\nimport android.content.Con"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/Platform.kt",
    "chars": 1093,
    "preview": "package com.matisse.utils\n\nimport android.content.Context\nimport android.content.pm.PackageManager\nimport android.os.Bui"
  },
  {
    "path": "matisse/src/main/java/com/matisse/utils/UIUtils.kt",
    "chars": 4652,
    "preview": "@file:JvmName(\"UIUtils\")\n\npackage com.matisse.utils\n\nimport android.content.Context\nimport android.content.res.Resources"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/CheckRadioView.kt",
    "chars": 2074,
    "preview": "package com.matisse.widget\n\nimport android.content.Context\nimport android.content.res.TypedArray\nimport android.graphics"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/CheckView.kt",
    "chars": 7746,
    "preview": "package com.matisse.widget\n\nimport android.content.Context\nimport android.content.res.TypedArray\nimport android.graphics"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/IncapableDialog.kt",
    "chars": 1222,
    "preview": "package com.matisse.widget\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AlertDialog\nimport androidx.fragment."
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/MediaGrid.kt",
    "chars": 3288,
    "preview": "package com.matisse.widget\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.text"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/MediaGridInset.kt",
    "chars": 1006,
    "preview": "package com.matisse.widget\n\nimport android.graphics.Rect\nimport android.view.View\nimport androidx.recyclerview.widget.Re"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/PreviewViewPager.kt",
    "chars": 640,
    "preview": "package com.matisse.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.View\nimp"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/SquareFrameLayout.kt",
    "chars": 589,
    "preview": "package com.matisse.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.FrameL"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/CompatDecoderFactory.java",
    "chars": 562,
    "preview": "package com.matisse.widget.longimage;\n\n\nimport androidx.annotation.NonNull;\n\n/**\n * Compatibility factory to instantiate"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/DecoderFactory.java",
    "chars": 386,
    "preview": "package com.matisse.widget.longimage;\n\n/**\n * Interface for decoder (and region decoder) factories.\n * @param <T> the cl"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/ImageDecoder.java",
    "chars": 991,
    "preview": "package com.matisse.widget.longimage;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.ne"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/ImageRegionDecoder.java",
    "chars": 2051,
    "preview": "package com.matisse.widget.longimage;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.gr"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/ImageSource.java",
    "chars": 7167,
    "preview": "package com.matisse.widget.longimage;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Rect;\nimport android.net."
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/ImageViewState.java",
    "chars": 1367,
    "preview": "/*\nCopyright 2014 David Morrissey\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this "
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/SkiaImageDecoder.java",
    "chars": 3398,
    "preview": "package com.matisse.widget.longimage;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport an"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/SkiaImageRegionDecoder.java",
    "chars": 4264,
    "preview": "package com.matisse.widget.longimage;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport an"
  },
  {
    "path": "matisse/src/main/java/com/matisse/widget/longimage/SubsamplingScaleImageView.java",
    "chars": 130013,
    "preview": "/*\nCopyright 2013-2015 David Morrissey\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use "
  },
  {
    "path": "matisse/src/main/res/anim/bottom_down_out.xml",
    "chars": 399,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n "
  },
  {
    "path": "matisse/src/main/res/anim/bottom_up_in.xml",
    "chars": 470,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:inter"
  },
  {
    "path": "matisse/src/main/res/anim/ucrop_anim_fade_in.xml",
    "chars": 194,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<alpha xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:dur"
  },
  {
    "path": "matisse/src/main/res/anim/ucrop_close.xml",
    "chars": 517,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:inter"
  },
  {
    "path": "matisse/src/main/res/anim/ucrop_loader_circle_path.xml",
    "chars": 639,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <objectAnim"
  },
  {
    "path": "matisse/src/main/res/anim/ucrop_loader_circle_scale.xml",
    "chars": 904,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <objectAnim"
  },
  {
    "path": "matisse/src/main/res/color/selector_base_text.xml",
    "chars": 408,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item a"
  },
  {
    "path": "matisse/src/main/res/color/selector_black_text.xml",
    "chars": 432,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item a"
  },
  {
    "path": "matisse/src/main/res/color/selector_white_text.xml",
    "chars": 388,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item a"
  },
  {
    "path": "matisse/src/main/res/color/ucrop_scale_text_view_selector.xml",
    "chars": 266,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item a"
  },
  {
    "path": "matisse/src/main/res/drawable/ucrop_gif_bg.xml",
    "chars": 317,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:sha"
  },
  {
    "path": "matisse/src/main/res/drawable/ucrop_oval_true.xml",
    "chars": 352,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:sha"
  },
  {
    "path": "matisse/src/main/res/drawable/ucrop_shadow_upside.xml",
    "chars": 245,
    "preview": "<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n    <gradient\n      "
  },
  {
    "path": "matisse/src/main/res/drawable/ucrop_vector_ic_crop.xml",
    "chars": 569,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:width=\"24dp\"\n        android:height=\""
  },
  {
    "path": "matisse/src/main/res/drawable/ucrop_vector_loader.xml",
    "chars": 895,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        andro"
  },
  {
    "path": "matisse/src/main/res/drawable/ucrop_vector_loader_animated.xml",
    "chars": 417,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<animated-vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n     "
  },
  {
    "path": "matisse/src/main/res/drawable-xhdpi/transparent.xml",
    "chars": 116,
    "preview": "<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"#00fdfdfd\" />\n</shape>\n"
  },
  {
    "path": "matisse/src/main/res/layout/activity_matisse.xml",
    "chars": 1945,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "matisse/src/main/res/layout/activity_media_preview.xml",
    "chars": 1100,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andro"
  },
  {
    "path": "matisse/src/main/res/layout/dialog_bottom_sheet.xml",
    "chars": 650,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schema"
  },
  {
    "path": "matisse/src/main/res/layout/dialog_bottom_sheet_folder.xml",
    "chars": 363,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android."
  },
  {
    "path": "matisse/src/main/res/layout/fragment_media_selection.xml",
    "chars": 497,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android."
  },
  {
    "path": "matisse/src/main/res/layout/fragment_picture_preview_item.xml",
    "chars": 917,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns"
  },
  {
    "path": "matisse/src/main/res/layout/fragment_preview_item.xml",
    "chars": 749,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns"
  },
  {
    "path": "matisse/src/main/res/layout/include_view_bottom.xml",
    "chars": 3580,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "matisse/src/main/res/layout/include_view_navigation.xml",
    "chars": 1841,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "matisse/src/main/res/layout/item_album_folder.xml",
    "chars": 2001,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
  },
  {
    "path": "matisse/src/main/res/layout/item_media_grid.xml",
    "chars": 262,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.matisse.widget.MediaGrid xmlns:android=\"http://schemas.android.com/apk/res/a"
  },
  {
    "path": "matisse/src/main/res/layout/item_photo_capture.xml",
    "chars": 787,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.matisse.widget.SquareFrameLayout xmlns:android=\"http://schemas.android.com/a"
  },
  {
    "path": "matisse/src/main/res/layout/ucrop_activity_photobox.xml",
    "chars": 875,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/ucrop_photobox\"\n    androi"
  },
  {
    "path": "matisse/src/main/res/layout/ucrop_aspect_ratio.xml",
    "chars": 217,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout style=\"@style/ucrop_WrapperIconState\">\n\n    <com.matisse.ucrop.view."
  },
  {
    "path": "matisse/src/main/res/layout/ucrop_layout_rotate_wheel.xml",
    "chars": 1793,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      "
  },
  {
    "path": "matisse/src/main/res/layout/ucrop_layout_scale_wheel.xml",
    "chars": 791,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      "
  },
  {
    "path": "matisse/src/main/res/layout/ucrop_picture_activity_multi_cutting.xml",
    "chars": 1386,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/ucrop_mulit_photobox\"\n  "
  },
  {
    "path": "matisse/src/main/res/layout/ucrop_picture_gf_adapter_edit_list.xml",
    "chars": 1847,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ Copyright (C) 2014 pengjianbo(pengjianbosoft@gmail.com), Inc.\n  ~\n  ~ Lic"
  },
  {
    "path": "matisse/src/main/res/layout/ucrop_view.xml",
    "chars": 726,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:"
  },
  {
    "path": "matisse/src/main/res/layout/view_media_grid_content.xml",
    "chars": 1230,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <ImageVie"
  },
  {
    "path": "matisse/src/main/res/menu/ucrop_menu_activity.xml",
    "chars": 553,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      xmlns:app="
  },
  {
    "path": "matisse/src/main/res/values/attrs.xml",
    "chars": 3616,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <attr name=\"Navigation_height\" format=\"dimension\" />\n    <attr na"
  },
  {
    "path": "matisse/src/main/res/values/colors.xml",
    "chars": 1549,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"white\">#FFFFFF</color>\n    <color name=\"white_press\""
  },
  {
    "path": "matisse/src/main/res/values/colors_default.xml",
    "chars": 989,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"primary\">@color/white</color>\n    <color name=\"prima"
  },
  {
    "path": "matisse/src/main/res/values/dimens.xml",
    "chars": 1835,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"media_grid_size\">48dp</dimen>\n    <dimen name=\"media"
  },
  {
    "path": "matisse/src/main/res/values/ids.xml",
    "chars": 180,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <item name=\"id_rec"
  },
  {
    "path": "matisse/src/main/res/values/long_attrs.xml",
    "chars": 1022,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nCopyright 2014 David Morrissey\n\nLicensed under the Apache License, Version 2"
  },
  {
    "path": "matisse/src/main/res/values/strings.xml",
    "chars": 2161,
    "preview": "<resources>\n    <string name=\"album_name_all\">All Media</string>\n    <string name=\"button_ok\">OK</string>\n    <string na"
  },
  {
    "path": "matisse/src/main/res/values/styles.xml",
    "chars": 5931,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"Animation.Bottom\" parent=\"Theme.AppCompat.Light.Dia"
  },
  {
    "path": "matisse/src/main/res/values/values.xml",
    "chars": 131,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"ucrop_progress_loading_anim_time\">1500</integer>\n<"
  },
  {
    "path": "matisse/src/main/res/values-zh/strings.xml",
    "chars": 1729,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"ExtraTran"
  },
  {
    "path": "matisse/src/test/java/com/matisse/ExampleUnitTest.java",
    "chars": 383,
    "preview": "package com.matisse;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * Example local unit te"
  },
  {
    "path": "settings.gradle",
    "chars": 27,
    "preview": "include ':app', ':matisse'\n"
  }
]

About this extraction

This page contains the full source code of the NFLeo/Matisse-Kotlin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 197 files (797.4 KB), approximately 189.3k tokens, and a symbol index with 924 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!