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
================================================
[  ](https://bintray.com/nfleo/MatisseKotlin/MatisseKotlin/2.1.1/link)

[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 |
|:------------------------------:|:---------------------------------:|:--------------------------------:|
| |  | |
| Circle Crop | Square Crop |
|:------------------------------:|:---------------------------------:|
| |  |
#### Simple usage snippet
------
### 配置主题.
| Media theme | Preview theme |
|:------------------------------:|:---------------------------------:|
| |  |
使用套路与原项目一致,只是多增加了一些参数,另外定制时需配置所提供的所有参数。
使用主题时可直接使用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
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
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": "[  ](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.