├── .gitignore ├── LICENSE ├── README.md ├── README_CN.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── coco_demo.webp │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── qw │ │ └── soulphototaker │ │ ├── BaseToolbarActivity.kt │ │ ├── CropActivity.kt │ │ ├── DebugDefaultDisposerActivity.kt │ │ ├── DisposeActivity.kt │ │ ├── MainActivity.kt │ │ ├── PickPictureActivity.kt │ │ └── TakePictureActivity.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_launcher_background.xml │ └── shape_pill_white_hollow.xml │ ├── layout │ ├── activity_crop_photo.xml │ ├── activity_dispose.xml │ ├── activity_funtion_detail.xml │ ├── activity_main.xml │ ├── activity_pick_photo.xml │ ├── activity_take_photo.xml │ ├── fragment_coco.xml │ ├── view_guide_1.xml │ └── view_guide_2.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── coco ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── qw │ │ └── photo │ │ ├── CoCo.kt │ │ ├── CoCoConfigs.kt │ │ ├── CoCoFileProvider.kt │ │ ├── DevUtil.kt │ │ ├── Utils.kt │ │ ├── agent │ │ ├── AcceptActivityResultHandlerFactory.kt │ │ ├── AcceptResultFragment.kt │ │ ├── AcceptResultSupportFragment.kt │ │ └── IContainer.kt │ │ ├── annotations │ │ └── Annotations.kt │ │ ├── callback │ │ ├── BaseFunctionCallBack.kt │ │ ├── CoCoAdapter.kt │ │ ├── CoCoCallBack.kt │ │ ├── CompressListener.kt │ │ ├── CropCallBack.kt │ │ ├── DisposeAdapter.kt │ │ ├── DisposeCallBack.kt │ │ ├── PickAdapter.kt │ │ ├── PickCallBack.kt │ │ ├── TakeAdapter.kt │ │ └── TakeCallBack.kt │ │ ├── constant │ │ ├── CompressStrategy.kt │ │ ├── Constant.kt │ │ ├── Face.kt │ │ ├── HostStatus.kt │ │ ├── Range.kt │ │ └── Type.kt │ │ ├── dispose │ │ ├── CompressFactory.kt │ │ ├── ICompress.kt │ │ ├── MatrixCompressor.kt │ │ ├── QualityCompressor.kt │ │ ├── WorkThread.kt │ │ └── disposer │ │ │ ├── DefaultImageDisposer.kt │ │ │ └── Disposer.kt │ │ ├── exception │ │ └── Exceptions.kt │ │ ├── functions │ │ ├── BaseFunctionBuilder.kt │ │ ├── CropBuilder.kt │ │ ├── DisposeBuilder.kt │ │ ├── PickBuilder.kt │ │ └── TakeBuilder.kt │ │ ├── pojo │ │ └── Result.kt │ │ └── work │ │ ├── BaseWorker.kt │ │ ├── CropWorker.kt │ │ ├── DisposeWorker.kt │ │ ├── FunctionManager.kt │ │ ├── PickPhotoWorker.kt │ │ ├── TakePhotoWorker.kt │ │ └── Worker.kt │ └── res │ └── xml │ └── coco_provider_path.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | <<<<<<< HEAD 2 | /build 3 | 4 | ======= 5 | >>>>>>> 1475e57f0e654f09b13386ef8c226e88914dd513 6 | # Built application files 7 | *.apk 8 | *.ap_ 9 | 10 | <<<<<<< HEAD 11 | # Files for the Dalvik VM 12 | ======= 13 | # Files for the ART/Dalvik VM 14 | >>>>>>> 1475e57f0e654f09b13386ef8c226e88914dd513 15 | *.dex 16 | 17 | # Java class files 18 | *.class 19 | 20 | # Generated files 21 | bin/ 22 | gen/ 23 | <<<<<<< HEAD 24 | ======= 25 | out/ 26 | >>>>>>> 1475e57f0e654f09b13386ef8c226e88914dd513 27 | 28 | # Gradle files 29 | .gradle/ 30 | build/ 31 | 32 | # Local configuration file (sdk path, etc) 33 | local.properties 34 | 35 | # Proguard folder generated by Eclipse 36 | proguard/ 37 | 38 | # Log Files 39 | *.log 40 | 41 | # Android Studio Navigation editor temp files 42 | .navigation/ 43 | 44 | # Android Studio captures folder 45 | captures/ 46 | 47 | <<<<<<< HEAD 48 | # Windows thumbnail db 49 | Thumbs.db 50 | 51 | # OSX files 52 | .DS_Store 53 | 54 | # Eclipse project files 55 | .classpath 56 | .project 57 | 58 | # Android Studio 59 | *.iml 60 | .idea 61 | 62 | #NDK 63 | obj/ 64 | ======= 65 | # IntelliJ 66 | *.iml 67 | .idea/workspace.xml 68 | .idea/tasks.xml 69 | .idea/gradle.xml 70 | .idea/assetWizardSettings.xml 71 | .idea/dictionaries 72 | .idea/libraries 73 | .idea/caches 74 | 75 | # Keystore files 76 | # Uncomment the following line if you do not want to check your keystore files in. 77 | #*.jks 78 | 79 | # External native build folder generated in Android Studio 2.2 and later 80 | .externalNativeBuild 81 | 82 | # Google Services (e.g. APIs or Firebase) 83 | google-services.json 84 | 85 | # Freeline 86 | freeline.py 87 | freeline/ 88 | freeline_project_description.json 89 | 90 | # fastlane 91 | fastlane/report.xml 92 | fastlane/Preview.html 93 | fastlane/screenshots 94 | fastlane/test_output 95 | fastlane/readme.md 96 | >>>>>>> 1475e57f0e654f09b13386ef8c226e88914dd513 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoCo 2 | [![Hex.pm](https://img.shields.io/badge/download-1.1.6-green)](https://www.apache.org/licenses/LICENSE-2.0) 3 | [![Hex.pm](https://img.shields.io/badge/Api-4.0%2B-yellow)]() 4 | [![Hex.pm](https://img.shields.io/hexpm/l/plug.svg)]() 5 | [![Hex.pm](https://img.shields.io/badge/Jetpack-AndroidX-red)]() 6 | ### [中文版说明](https://github.com/soulqw/CoCo/blob/master/README_CN.md) 7 | #### An delicate lib for System Capture, Pick and Crop in Android 🐵 8 | - Provided the function of image capture or album selection or cropping from system application just need one code 9 | - The inner adjust the FileProvider in Android 7.0 and above 10 | - The default image disposer provided two strategy to compress the image, it can also can be customed 11 | - Both Activity and Fragment supported,the asynchronous dispose of image will bind their lifecycle automatic 12 | - The new designed api, easy to understand 13 | - Fully based on kotlin, also can worked with java 14 | - Already migrated to AndroidX, simplify to use 15 | - Android Scoped-Storage Support and can work on Android 11 and higher 16 | ## Installation: 17 | 18 | ![image](https://img-blog.csdnimg.cn/20191009181659912.png) 19 | 20 | ```java 21 | dependencies { 22 | implementation 'com.github.soulqw:CoCo:1.1.6' 23 | } 24 | ``` 25 | ###### The new in Release : 26 | - The Pick function in the Range.PICK_CONTENT model can filter the specific file type such as PNG、JPG、GIF and so on, the default config is no filter 27 | - Add the default abstract class CoCoAdapter 28 | 29 | ## Usage: 30 | #### basic usage 31 | 32 | - Capture an image from system camera 33 | ```kotlin 34 | CoCo.with(this@MainActivity) 35 | .take(createSDCardFile()) 36 | .start(object : CoCoAdapter() { 37 | 38 | override fun onSuccess(data: TakeResult) { 39 | iv_image.setImageBitmap(Utils.getBitmapFromFile(data.savedFile!!.absolutePath)) 40 | } 41 | }) 42 | ``` 43 | image sample: 44 | 45 | ![image](https://cdn.nlark.com/yuque/0/2020/gif/1502571/1601093298091-b091b479-05d0-435e-a650-ba5e07850d72.gif) 46 | 47 | - Pick an image from system Gallery: 48 | 49 | ```kotlin 50 | CoCo.with(this@MainActivity) 51 | .pick() 52 | .start(object : CoCoAdapter() { 53 | override fun onSuccess(data: PickResult) { 54 | 55 | iv_image.setImageURI(data.originUri) 56 | 57 | } 58 | }) 59 | ``` 60 | ##### image sample: 61 | 62 | ![image](https://cdn.nlark.com/yuque/0/2020/gif/1502571/1601093668141-533ce509-9f4e-45fa-99c7-57a9a3d31335.gif) 63 | 64 | - Dispose the origin image: 65 | Generally speaking, we may need dispose the origin image such as compress and so on, so CoCo provide the operator of dispose, we can use it to dispose the image 66 | ```kotlin 67 | //pick then dispose 68 | CoCo.with(this) 69 | .pick() 70 | //switch the operator 71 | .then() 72 | .dispose() 73 | .start(object : CoCoAdapter() { 74 | override fun onSuccess(data: DisposeResult) { 75 | iv_image.setImageBitmap(data.compressBitmap) 76 | } 77 | }) 78 | 79 | ``` 80 | We use the "then" method to switch the operator, it can also combination the another operators 81 | 82 | ##### dispose operator: 83 | 84 | dispose operator can dispose the file in background thread automatic, it can also bind the related container,s lifecycle in method "with()" 85 | 86 | ###### It not only can works with other operators: 87 | ```kotlin 88 | CoCo.with(this) 89 | .take(createSDCardFile()) 90 | .then() 91 | .dispose() 92 | .start(object : CoCoAdapter() { 93 | 94 | override fun onSuccess(data: DisposeResult) { 95 | iv_image.setImageBitmap(Utils.getBitmapFromFile(data.savedFile!!.absolutePath)) 96 | } 97 | }) 98 | ``` 99 | ###### It also can works separately: 100 | ```kotlin 101 | CoCo.with(this) 102 | .dispose() 103 | .origin(imageFile.path) 104 | .start(object : CoCoAdapter() { 105 | 106 | override fun onSuccess(data: DisposeResult) { 107 | iv_image.setImageBitmap(data.compressBitmap) 108 | } 109 | }) 110 | ``` 111 | ###### We can use customDisposer to dispose the image,we can also custom the disposer: 112 | 113 | ```kotlin 114 | CoCo.with(this) 115 | .dispose() 116 | .disposer(CustomDisposer()) 117 | //.disposer(DefaultImageDisposer()) 118 | .origin(imageFile.path) 119 | .start(object : CoCoAdapter() { 120 | 121 | override fun onSuccess(data: DisposeResult) { 122 | iv_image.setImageBitmap(data.compressBitmap) 123 | } 124 | }) 125 | 126 | /** 127 | * custom disposer 128 | * rotation image 129 | */ 130 | class CustomDisposer : Disposer { 131 | override fun disposeFile(originPath: String, targetToSaveResult: File?): DisposeResult { 132 | return DisposeResult().also { 133 | var bitmap = QualityCompressor() 134 | .compress(originPath, 80) 135 | val m = Matrix() 136 | m.postRotate(90f) 137 | bitmap = Bitmap.createBitmap( 138 | bitmap!!, 0, 0, bitmap.width, 139 | bitmap.height, m, true 140 | ) 141 | it.savedFile = targetToSaveResult 142 | it.compressBitmap = bitmap 143 | } 144 | } 145 | } 146 | 147 | ``` 148 | #### The Crop operator: 149 | We can provide an image to system Crop: 150 | 151 | ```kotlin 152 | CoCo.with(this@CropActivity) 153 | .crop(imageFile) 154 | .start(object : CoCoAdapter() { 155 | 156 | override fun onSuccess(data: CropResult) { 157 | iv_image.setImageBitmap(data.cropBitmap) 158 | } 159 | 160 | }) 161 | ``` 162 | Of course, it can works with other combinations: 163 | 164 | ```kotlin 165 | CoCo.with(this@MainActivity) 166 | .pick() 167 | .then() 168 | .crop() 169 | .start(object : CoCoAdapter() { 170 | 171 | override fun onSuccess(data: CropResult) { 172 | iv_image.setImageBitmap(data.cropBitmap) 173 | } 174 | }) 175 | ``` 176 | image sample: 177 | 178 | ![image](https://upload-images.jianshu.io/upload_images/11595074-7fba783db175f9ed.gif?imageMogr2/auto-orient/strip) 179 | 180 | - Another functions: 181 | ###### every operator can add the call backs: 182 | 183 | ```kotlin 184 | CoCo.with(this@PickPictureActivity) 185 | .pick() 186 | .range(Range.PICK_CONTENT) 187 | // .range(Range.PICK_DICM) 188 | .callBack(object : PickCallBack { 189 | 190 | override fun onFinish(result: PickResult) { 191 | Log.d(MainActivity.TAG, "pick onFinish${result}") 192 | } 193 | 194 | override fun onCancel() { 195 | Log.d(MainActivity.TAG, "pick onCancel") 196 | } 197 | 198 | override fun onStart() { 199 | Log.d(MainActivity.TAG, "pick onStart") 200 | } 201 | 202 | }).start(object : CoCoCallBack { 203 | 204 | override fun onSuccess(data: PickResult) { 205 | iv_image.setImageURI(data.originUri) 206 | } 207 | 208 | override fun onFailed(exception: Exception) {} 209 | }) 210 | ``` 211 | - the flow image: 212 | ![image](https://img-blog.csdnimg.cn/20201205192149134.png) 213 | 214 | More detail can use the demo 215 | 216 | ### The screenshot: 217 | ![image](https://cdn.nlark.com/yuque/0/2020/png/1502571/1601094243032-2d14deb1-e487-4d6e-906e-fafe6845c654.png) 218 | 219 | #### [The blog for this lib](https://juejin.cn/post/6907620425837051917) 220 | 221 | 222 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # CoCo 2 | [![Hex.pm](https://img.shields.io/badge/download-1.1.6-green)](https://www.apache.org/licenses/LICENSE-2.0) 3 | [![Hex.pm](https://img.shields.io/badge/Api-4.0%2B-yellow)]() 4 | [![Hex.pm](https://img.shields.io/hexpm/l/plug.svg)]() 5 | [![Hex.pm](https://img.shields.io/badge/Jetpack-AndroidX-red)]() 6 | #### 一款小而美的的Android系统相机拍照、系统相册选择、裁剪库🐵 7 | - 一行代码完成从系统相机拍照、系统相册选择图片、系统裁剪 8 | - 内部适配 7.0 FileProvider文件处理,无需自己额外处理 9 | - 默认图片处理器自带两种图片压缩策略,并可按需自定义图片处理器 10 | - 支持Activity、Fragment,图片异步处理自动绑定相关容器生命周期 11 | - 全新设计的APi-更灵活-更易于理解 12 | - 完全基于Kotlin编写,与Java兼容 13 | - 全面适配AndroidX、配置简单,导入方便 14 | - 适配范围存储,也可以运行在Android 11 以及更高版本上 15 | ## Installation: 16 | 17 | ![image](https://img-blog.csdnimg.cn/20191009181659912.png) 18 | 19 | ```java 20 | dependencies { 21 | implementation 'com.github.soulqw:CoCo:1.1.6' 22 | } 23 | ``` 24 | ###### 最新Release 改动: 25 | - Pick 选图在 Range.PICK_CONTENT 模式下可以指定过滤文件类型,如PNG、JPG、GIF等,默认所有格式 26 | - CoCo 默认回调增加抽象类实现 CoCoAdapter 27 | 28 | CoCo 1.0.0 + 将迎来历史上最大的更新: 29 | 30 | 强烈建议您迁移到最新的APi,方便后续新功能的拓展,老版本最后Release 版本将维护至[0.3.1](https://github.com/soulqw/CoCo/blob/developer/README_OLD.md),后续不再更新(分支master_1.0.0_below)。 31 | 32 | ## Usage: 33 | #### 基本用法 34 | 35 | - 调用系统相机拍照 36 | ```kotlin 37 | CoCo.with(this@MainActivity) 38 | .take(createSDCardFile()) 39 | .start(object : CoCoAdapter() { 40 | 41 | override fun onSuccess(data: TakeResult) { 42 | iv_image.setImageBitmap(Utils.getBitmapFromFile(data.savedFile!!.absolutePath)) 43 | } 44 | }) 45 | ``` 46 | 效果图: 47 | 48 | ![image](https://cdn.nlark.com/yuque/0/2020/gif/1502571/1601093298091-b091b479-05d0-435e-a650-ba5e07850d72.gif) 49 | 50 | - 调用系统相册选择图片: 51 | 52 | ```kotlin 53 | CoCo.with(this@MainActivity) 54 | .pick() 55 | .start(object : CoCoAdapter() { 56 | override fun onSuccess(data: PickResult) { 57 | 58 | iv_image.setImageURI(data.originUri) 59 | 60 | } 61 | }) 62 | ``` 63 | ##### 效果图: 64 | 65 | ![image](https://cdn.nlark.com/yuque/0/2020/gif/1502571/1601093668141-533ce509-9f4e-45fa-99c7-57a9a3d31335.gif) 66 | 67 | - 处理我们拿到的原图: 68 | 69 | 上述以上是原图的情形,通常情况下,我们常常要对原图做一些处理,比如压缩等,所以CoCo 提供了dispose操作符,方便获得图片之后做一些处理: 70 | ```kotlin 71 | //选择图片后压缩 72 | CoCo.with(this) 73 | .pick() 74 | //切换操作符 75 | .then() 76 | .dispose() 77 | .start(object : CoCoAdapter() { 78 | override fun onSuccess(data: DisposeResult) { 79 | iv_image.setImageBitmap(data.compressBitmap) 80 | } 81 | }) 82 | 83 | ``` 84 | 我们通过 then 操作符来完成操作符的组合,可以进行一些列操作符的串联流式处理。 85 | 86 | ##### dispose 操作符: 87 | 88 | dispose操作符可以自动在子线程处理我们要处理的文件,并且自动绑定with()容器中的生命周期 89 | 90 | ###### 它不仅可以和其它操作符组合使用: 91 | ```kotlin 92 | CoCo.with(this) 93 | .take(createSDCardFile()) 94 | .then() 95 | .dispose() 96 | .start(object : CoCoAdapter() { 97 | 98 | override fun onSuccess(data: DisposeResult) { 99 | iv_image.setImageBitmap(Utils.getBitmapFromFile(data.savedFile!!.absolutePath)) 100 | } 101 | }) 102 | ``` 103 | ###### 它还可以单独使用: 104 | ```kotlin 105 | CoCo.with(this) 106 | .dispose() 107 | .origin(imageFile.path) 108 | .start(object : CoCoAdapter() { 109 | 110 | override fun onSuccess(data: DisposeResult) { 111 | iv_image.setImageBitmap(data.compressBitmap) 112 | } 113 | }) 114 | ``` 115 | ###### 系统默认Default 图片处理器可以帮我们完成图片处理,也可自定义处理逻辑: 116 | 117 | ```kotlin 118 | CoCo.with(this) 119 | .dispose() 120 | .disposer(CustomDisposer()) 121 | //.disposer(DefaultImageDisposer()) 122 | .origin(imageFile.path) 123 | .start(object : CoCoAdapter() { 124 | 125 | override fun onSuccess(data: DisposeResult) { 126 | iv_image.setImageBitmap(data.compressBitmap) 127 | } 128 | }) 129 | 130 | /** 131 | * custom disposer 132 | * rotation image 133 | */ 134 | class CustomDisposer : Disposer { 135 | override fun disposeFile(originPath: String, targetToSaveResult: File?): DisposeResult { 136 | return DisposeResult().also { 137 | var bitmap = QualityCompressor() 138 | .compress(originPath, 80) 139 | val m = Matrix() 140 | m.postRotate(90f) 141 | bitmap = Bitmap.createBitmap( 142 | bitmap!!, 0, 0, bitmap.width, 143 | bitmap.height, m, true 144 | ) 145 | it.savedFile = targetToSaveResult 146 | it.compressBitmap = bitmap 147 | } 148 | } 149 | } 150 | 151 | ``` 152 | #### Crop操作符: 153 | 让我可以指定一个图片文件提供给系统裁剪处理: 154 | 155 | ```kotlin 156 | CoCo.with(this@CropActivity) 157 | .crop(imageFile) 158 | .start(object : CoCoAdapter() { 159 | 160 | override fun onSuccess(data: CropResult) { 161 | iv_image.setImageBitmap(data.cropBitmap) 162 | } 163 | 164 | }) 165 | ``` 166 | 当然,也可以组合原有操作符一起使用: 167 | 168 | ```kotlin 169 | CoCo.with(this@MainActivity) 170 | .pick() 171 | .then() 172 | .crop() 173 | .start(object : CoCoAdapter() { 174 | 175 | override fun onSuccess(data: CropResult) { 176 | iv_image.setImageBitmap(data.cropBitmap) 177 | } 178 | }) 179 | ``` 180 | 效果图: 181 | 182 | ![image](https://upload-images.jianshu.io/upload_images/11595074-7fba783db175f9ed.gif?imageMogr2/auto-orient/strip) 183 | 184 | - 其它功能: 185 | ###### 每个操作符都可以添加回调监听: 186 | 187 | ```kotlin 188 | CoCo.with(this@PickPictureActivity) 189 | .pick() 190 | .range(Range.PICK_CONTENT) 191 | // .range(Range.PICK_DICM) 192 | .callBack(object : PickCallBack { 193 | 194 | override fun onFinish(result: PickResult) { 195 | Log.d(MainActivity.TAG, "pick onFinish${result}") 196 | } 197 | 198 | override fun onCancel() { 199 | Log.d(MainActivity.TAG, "pick onCancel") 200 | } 201 | 202 | override fun onStart() { 203 | Log.d(MainActivity.TAG, "pick onStart") 204 | } 205 | 206 | }).start(object : CoCoCallBack { 207 | 208 | override fun onSuccess(data: PickResult) { 209 | iv_image.setImageURI(data.originUri) 210 | } 211 | 212 | override fun onFailed(exception: Exception) {} 213 | }) 214 | ``` 215 | - 半流程图: 216 | ![image](https://img-blog.csdnimg.cn/20201205192149134.png) 217 | 218 | 更多功能可参考Demo 219 | 220 | ### 截图: 221 | ![image](https://cdn.nlark.com/yuque/0/2020/png/1502571/1601094243032-2d14deb1-e487-4d6e-906e-fafe6845c654.png) 222 | 223 | #### [原理和详细设计分享可参考这里](https://juejin.cn/post/6907620425837051917) 224 | 225 | ##### 联系我(注明来意): 226 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210209234455912.png) 227 | 228 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | <<<<<<< HEAD 2 | /build 3 | 4 | ======= 5 | >>>>>>> 1475e57f0e654f09b13386ef8c226e88914dd513 6 | # Built application files 7 | *.apk 8 | *.ap_ 9 | 10 | <<<<<<< HEAD 11 | # Files for the Dalvik VM 12 | ======= 13 | # Files for the ART/Dalvik VM 14 | >>>>>>> 1475e57f0e654f09b13386ef8c226e88914dd513 15 | *.dex 16 | 17 | # Java class files 18 | *.class 19 | 20 | # Generated files 21 | bin/ 22 | gen/ 23 | <<<<<<< HEAD 24 | ======= 25 | out/ 26 | >>>>>>> 1475e57f0e654f09b13386ef8c226e88914dd513 27 | 28 | # Gradle files 29 | .gradle/ 30 | build/ 31 | 32 | # Local configuration file (sdk path, etc) 33 | local.properties 34 | 35 | # Proguard folder generated by Eclipse 36 | proguard/ 37 | 38 | # Log Files 39 | *.log 40 | 41 | # Android Studio Navigation editor temp files 42 | .navigation/ 43 | 44 | # Android Studio captures folder 45 | captures/ 46 | 47 | <<<<<<< HEAD 48 | # Windows thumbnail db 49 | Thumbs.db 50 | 51 | # OSX files 52 | .DS_Store 53 | 54 | # Eclipse project files 55 | .classpath 56 | .project 57 | 58 | # Android Studio 59 | *.iml 60 | .idea 61 | 62 | #NDK 63 | obj/ 64 | ======= 65 | # IntelliJ 66 | *.iml 67 | .idea/workspace.xml 68 | .idea/tasks.xml 69 | .idea/gradle.xml 70 | .idea/assetWizardSettings.xml 71 | .idea/dictionaries 72 | .idea/libraries 73 | .idea/caches 74 | 75 | # Keystore files 76 | # Uncomment the following line if you do not want to check your keystore files in. 77 | #*.jks 78 | 79 | # External native build folder generated in Android Studio 2.2 and later 80 | .externalNativeBuild 81 | 82 | # Google Services (e.g. APIs or Firebase) 83 | google-services.json 84 | 85 | # Freeline 86 | freeline.py 87 | freeline/ 88 | freeline_project_description.json 89 | 90 | # fastlane 91 | fastlane/report.xml 92 | fastlane/Preview.html 93 | fastlane/screenshots 94 | fastlane/test_output 95 | fastlane/readme.md 96 | >>>>>>> 1475e57f0e654f09b13386ef8c226e88914dd513 97 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 33 7 | defaultConfig { 8 | applicationId "com.qw.soulphototaker" 9 | minSdkVersion 14 10 | targetSdkVersion 33 11 | versionCode 26 12 | versionName "1.1.2" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | } 18 | } 19 | namespace 'com.qw.soulphototaker' 20 | } 21 | 22 | dependencies { 23 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 24 | implementation 'androidx.appcompat:appcompat:1.6.1' 25 | // implementation project(':coco') 26 | implementation 'com.github.soulqw:CoCo:1.1.6' 27 | implementation 'com.github.soulqw:SoulPermission:1.3.1' 28 | implementation 'com.github.soulqw:Curtain:0.2.1' 29 | 30 | } 31 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 35 | 39 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/assets/coco_demo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulqw/CoCo/67103e69dd0d51819cd41a1b3e163a6030f12472/app/src/main/assets/coco_demo.webp -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soulqw/CoCo/67103e69dd0d51819cd41a1b3e163a6030f12472/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/qw/soulphototaker/BaseToolbarActivity.kt: -------------------------------------------------------------------------------- 1 | package com.qw.soulphototaker 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import java.io.File 5 | import java.io.IOException 6 | import java.text.SimpleDateFormat 7 | import java.util.* 8 | 9 | open class BaseToolbarActivity : AppCompatActivity() { 10 | 11 | @Throws(IOException::class) 12 | fun createSDCardFile(): File { 13 | // Create an image file name 14 | val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) 15 | val storageDir = File(externalCacheDir!!.path + "/" + timeStamp) 16 | if (!storageDir.exists()) { 17 | storageDir.mkdir() 18 | } 19 | return File.createTempFile( 20 | "JPEG_${timeStamp}_demo", /* prefix */ 21 | ".jpg", /* suffix */ 22 | storageDir /* directory */ 23 | ) 24 | } 25 | 26 | @Throws(IOException::class) 27 | fun createSDCardFile(fileName: String?): File { 28 | // Create an image file name 29 | val storageDir = File(externalCacheDir!!.path + "/" + fileName) 30 | if (!storageDir.exists()) { 31 | storageDir.mkdir() 32 | } 33 | return File.createTempFile( 34 | "JPEG_test", 35 | ".jpg", /* suffix */ 36 | storageDir /* directory */ 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/qw/soulphototaker/CropActivity.kt: -------------------------------------------------------------------------------- 1 | package com.qw.soulphototaker 2 | 3 | import android.Manifest 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.widget.Toast 7 | import com.qw.photo.CoCo 8 | import com.qw.photo.callback.CoCoCallBack 9 | import com.qw.photo.callback.CropCallBack 10 | import com.qw.photo.pojo.CropResult 11 | import com.qw.soul.permission.SoulPermission 12 | import com.qw.soul.permission.bean.Permission 13 | import com.qw.soul.permission.callbcak.CheckRequestPermissionListener 14 | import kotlinx.android.synthetic.main.activity_crop_photo.* 15 | import kotlinx.android.synthetic.main.activity_funtion_detail.iv_image 16 | import kotlinx.android.synthetic.main.activity_take_photo.base 17 | import kotlinx.android.synthetic.main.activity_take_photo.others 18 | import kotlinx.android.synthetic.main.activity_take_photo.toolbar 19 | import java.io.File 20 | import java.io.FileOutputStream 21 | import java.io.InputStream 22 | 23 | 24 | /** 25 | * @author cd5160866 26 | */ 27 | class CropActivity : BaseToolbarActivity() { 28 | 29 | private lateinit var imageFile: File 30 | 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | super.onCreate(savedInstanceState) 33 | setContentView(R.layout.activity_crop_photo) 34 | setSupportActionBar(toolbar) 35 | title = "CropPhotoDetail" 36 | SoulPermission.getInstance() 37 | .checkAndRequestPermission( 38 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 39 | object : CheckRequestPermissionListener { 40 | 41 | override fun onPermissionOk(permission: Permission?) { 42 | createDefaultFile() 43 | } 44 | 45 | override fun onPermissionDenied(permission: Permission?) { 46 | Toast.makeText( 47 | applicationContext, 48 | "Need permission to start", 49 | Toast.LENGTH_SHORT 50 | ).show() 51 | finish() 52 | } 53 | }) 54 | 55 | //base usage 56 | base.setOnClickListener { 57 | CoCo.with(this@CropActivity) 58 | .crop(imageFile) 59 | .start(object : CoCoCallBack { 60 | 61 | override fun onSuccess(data: CropResult) { 62 | iv_image.setImageBitmap(data.cropBitmap) 63 | } 64 | 65 | override fun onFailed(exception: Exception) {} 66 | }) 67 | } 68 | 69 | //other functions in take operate 70 | others.setOnClickListener { 71 | CoCo.with(this@CropActivity) 72 | .crop(imageFile) 73 | .callBack(object : CropCallBack { 74 | 75 | override fun onFinish(result: CropResult) { 76 | Log.d(MainActivity.TAG, "crop onFinish $result") 77 | } 78 | 79 | override fun onCancel() { 80 | Log.d(MainActivity.TAG, "crop onCancel") 81 | } 82 | 83 | override fun onStart() { 84 | Log.d(MainActivity.TAG, "crop onStart") 85 | } 86 | 87 | }).start(object : CoCoCallBack { 88 | 89 | override fun onSuccess(data: CropResult) { 90 | iv_image.setImageBitmap(data.cropBitmap) 91 | } 92 | 93 | override fun onFailed(exception: Exception) {} 94 | }) 95 | } 96 | 97 | //work with other operate 1 98 | //pick photo first then crop image 99 | comb_1.setOnClickListener { 100 | CoCo.with(this@CropActivity) 101 | .pick() 102 | .then() 103 | .crop() 104 | .start(object : CoCoCallBack { 105 | 106 | override fun onSuccess(data: CropResult) { 107 | iv_image.setImageBitmap(data.cropBitmap) 108 | } 109 | 110 | override fun onFailed(exception: Exception) { 111 | } 112 | }) 113 | } 114 | 115 | //work with other operate 2 116 | //take then dispose then crop 117 | comb_2.setOnClickListener { 118 | CoCo.with(this@CropActivity) 119 | .take(createSDCardFile()) 120 | .then() 121 | .dispose() 122 | .then() 123 | .crop() 124 | .start(object : CoCoCallBack { 125 | 126 | override fun onSuccess(data: CropResult) { 127 | iv_image.setImageBitmap(data.cropBitmap) 128 | } 129 | 130 | override fun onFailed(exception: Exception) { 131 | } 132 | }) 133 | } 134 | } 135 | 136 | private fun createDefaultFile() { 137 | imageFile = createSDCardFile() 138 | createSdImageFile(imageFile.path) 139 | } 140 | 141 | /** 142 | * create a file from asset Path 143 | */ 144 | private fun createSdImageFile(fileName: String) { 145 | return try { 146 | val inStream: InputStream = resources.assets.open("coco_demo.webp") 147 | val outStream = FileOutputStream(fileName, true) 148 | val buffer = ByteArray(1024) 149 | var len = -1 150 | while (inStream.read(buffer).also { len = it } != -1) { 151 | outStream.write(buffer, 0, len) 152 | } 153 | outStream.close() 154 | inStream.close() 155 | } catch (e: java.lang.Exception) { 156 | e.printStackTrace() 157 | } 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /app/src/main/java/com/qw/soulphototaker/DebugDefaultDisposerActivity.kt: -------------------------------------------------------------------------------- 1 | package com.qw.soulphototaker 2 | 3 | import android.graphics.Bitmap 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.View 7 | import android.widget.SeekBar 8 | import android.widget.TextView 9 | import com.qw.curtain.lib.Curtain 10 | import com.qw.curtain.lib.CurtainFlow 11 | import com.qw.curtain.lib.flow.CurtainFlowInterface 12 | import com.qw.photo.CoCo 13 | import com.qw.photo.callback.CoCoCallBack 14 | import com.qw.photo.constant.CompressStrategy 15 | import com.qw.photo.dispose.disposer.DefaultImageDisposer 16 | import com.qw.photo.pojo.DisposeResult 17 | import kotlinx.android.synthetic.main.activity_funtion_detail.* 18 | import java.io.ByteArrayOutputStream 19 | 20 | 21 | /** 22 | * @author cd5160866 23 | */ 24 | class DebugDefaultDisposerActivity : BaseToolbarActivity() { 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | setContentView(R.layout.activity_funtion_detail) 29 | 30 | sb_degree.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 31 | override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { 32 | (findViewById(R.id.tv_degree) as TextView).text = progress.toString() + "" 33 | } 34 | 35 | override fun onStartTrackingTouch(seekBar: SeekBar) { 36 | 37 | } 38 | 39 | override fun onStopTrackingTouch(seekBar: SeekBar) { 40 | 41 | } 42 | }) 43 | 44 | btn_begin.setOnClickListener { 45 | val compressStrategy = if (rg_strategy.checkedRadioButtonId == R.id.matrix) { 46 | CompressStrategy.MATRIX 47 | } else { 48 | CompressStrategy.QUALITY 49 | } 50 | val degree = sb_degree.progress 51 | start(compressStrategy, degree) 52 | } 53 | 54 | showGuide() 55 | } 56 | 57 | private fun start(compressStrategy: CompressStrategy, degree: Int) { 58 | //build default disposer 59 | val disposer = DefaultImageDisposer() 60 | .strategy(compressStrategy) 61 | .degree(degree) 62 | 63 | CoCo.with(this) 64 | .take(createSDCardFile()) 65 | .then() 66 | .dispose(disposer) 67 | .start(object : CoCoCallBack { 68 | override fun onSuccess(data: DisposeResult) { 69 | iv_image.setImageBitmap(data.compressBitmap) 70 | tv_result.text = getImageSizeDesc(data.compressBitmap) 71 | } 72 | 73 | override fun onFailed(exception: Exception) { 74 | Log.d("CoCo", exception.toString()) 75 | } 76 | }) 77 | } 78 | 79 | private fun getImageSizeDesc(bitmap: Bitmap): String { 80 | val baos = ByteArrayOutputStream() 81 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos) 82 | val data = baos.toByteArray() 83 | return "图片:with: ${bitmap.width} height: ${bitmap.height}size: ${data.size}" 84 | } 85 | 86 | /** 87 | * https://github.com/soulqw/Curtain 88 | */ 89 | private fun showGuide() { 90 | 91 | val STEP1 = 1 92 | 93 | val STEP2 = 2 94 | 95 | CurtainFlow 96 | .Builder() 97 | .with(STEP1, Curtain(this) 98 | .with(rg_strategy) 99 | .setTopView(R.layout.view_guide_1)) 100 | .with(STEP2, Curtain(this) 101 | .with(sb_degree) 102 | .setTopView(R.layout.view_guide_2)) 103 | .create() 104 | .start(object : CurtainFlow.CallBack { 105 | 106 | override fun onProcess(currentId: Int, curtainFlow: CurtainFlowInterface?) { 107 | curtainFlow!!.findViewInCurrentCurtain(R.id.tv_i_know) 108 | .setOnClickListener { 109 | if (currentId == STEP1) { 110 | curtainFlow.push() 111 | } else { 112 | curtainFlow.finish() 113 | } 114 | } 115 | } 116 | 117 | override fun onFinish() { 118 | } 119 | }) 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/qw/soulphototaker/DisposeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.qw.soulphototaker 2 | 3 | import android.Manifest 4 | import android.content.Intent 5 | import android.graphics.Bitmap 6 | import android.graphics.Matrix 7 | import android.os.Bundle 8 | import android.util.Log 9 | import android.widget.Toast 10 | import com.qw.photo.CoCo 11 | import com.qw.photo.callback.CoCoCallBack 12 | import com.qw.photo.callback.DisposeCallBack 13 | import com.qw.photo.dispose.QualityCompressor 14 | import com.qw.photo.dispose.disposer.Disposer 15 | import com.qw.photo.pojo.DisposeResult 16 | import com.qw.soul.permission.SoulPermission 17 | import com.qw.soul.permission.bean.Permission 18 | import com.qw.soul.permission.callbcak.CheckRequestPermissionListener 19 | import kotlinx.android.synthetic.main.activity_dispose.* 20 | import java.io.File 21 | import java.io.FileOutputStream 22 | import java.io.InputStream 23 | 24 | /** 25 | * @author cd5160866 26 | */ 27 | class DisposeActivity : BaseToolbarActivity() { 28 | private lateinit var imageFile: File 29 | 30 | override fun onCreate(savedInstanceState: Bundle?) { 31 | super.onCreate(savedInstanceState) 32 | setContentView(R.layout.activity_dispose) 33 | setSupportActionBar(toolbar) 34 | title = "DisposeDetail" 35 | 36 | SoulPermission.getInstance() 37 | .checkAndRequestPermission( 38 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 39 | object : CheckRequestPermissionListener { 40 | 41 | override fun onPermissionOk(permission: Permission?) { 42 | createDefaultFile() 43 | initComponent() 44 | } 45 | 46 | override fun onPermissionDenied(permission: Permission?) { 47 | Toast.makeText( 48 | applicationContext, 49 | "Need permission to start", 50 | Toast.LENGTH_SHORT 51 | ).show() 52 | finish() 53 | } 54 | }) 55 | } 56 | 57 | private fun createDefaultFile() { 58 | imageFile = createSDCardFile() 59 | createSdImageFile(imageFile.path) 60 | } 61 | 62 | private fun initComponent() { 63 | base.setOnClickListener { 64 | 65 | CoCo.with(this) 66 | .dispose() 67 | .origin(imageFile.path) 68 | .start(object : CoCoCallBack { 69 | 70 | override fun onSuccess(data: DisposeResult) { 71 | iv_image.setImageBitmap(data.compressBitmap) 72 | } 73 | 74 | override fun onFailed(exception: Exception) { 75 | Log.d(MainActivity.TAG, exception.toString()) 76 | } 77 | }) 78 | 79 | } 80 | 81 | others.setOnClickListener { 82 | CoCo.with(this) 83 | .dispose() 84 | .origin(imageFile.path) 85 | //custom to dispose image 86 | .disposer(CustomDisposer()) 87 | //if set the origin file and the result file will both keep 88 | .fileToSaveResult(createSDCardFile("resultFile")) 89 | .callBack(object : DisposeCallBack { 90 | override fun onFinish(result: DisposeResult) { 91 | Log.d(MainActivity.TAG, "dispose finish") 92 | } 93 | 94 | override fun onStart() { 95 | Log.d(MainActivity.TAG, "dispose start") 96 | } 97 | }) 98 | // final common callback 99 | .start(object : CoCoCallBack { 100 | override fun onSuccess(data: DisposeResult) { 101 | iv_image.setImageBitmap(data.compressBitmap) 102 | } 103 | 104 | override fun onFailed(exception: Exception) { 105 | Log.d(MainActivity.TAG, exception.toString()) 106 | } 107 | }) 108 | } 109 | 110 | comb_take.setOnClickListener { 111 | 112 | CoCo.with(this) 113 | .take(createSDCardFile()) 114 | .then() 115 | .dispose() 116 | // .disposer(CustomDisposer()) 117 | .start(object : CoCoCallBack { 118 | override fun onSuccess(data: DisposeResult) { 119 | iv_image.setImageBitmap(data.compressBitmap) 120 | } 121 | 122 | override fun onFailed(exception: Exception) { 123 | Log.d(MainActivity.TAG, exception.toString()) 124 | } 125 | }) 126 | 127 | } 128 | 129 | comb_pick.setOnClickListener { 130 | 131 | CoCo.with(this) 132 | .pick() 133 | .then() 134 | .dispose() 135 | //set if you want save the result to file 136 | // .fileToSaveResult(createSDCardFile()) 137 | .start(object : CoCoCallBack { 138 | override fun onSuccess(data: DisposeResult) { 139 | iv_image.setImageBitmap(data.compressBitmap) 140 | } 141 | 142 | override fun onFailed(exception: Exception) { 143 | Log.d(MainActivity.TAG, exception.toString()) 144 | } 145 | }) 146 | } 147 | 148 | debug_for_default_disposer.setOnClickListener { 149 | startActivity(Intent(this, DebugDefaultDisposerActivity::class.java)) 150 | } 151 | } 152 | 153 | /** 154 | * create a file from asset Path 155 | */ 156 | private fun createSdImageFile(fileName: String) { 157 | return try { 158 | val inStream: InputStream = resources.assets.open("ic_launcher_round.png") 159 | val outStream = FileOutputStream(fileName, true) 160 | val buffer = ByteArray(1024) 161 | var len = -1 162 | while (inStream.read(buffer).also { len = it } != -1) { 163 | outStream.write(buffer, 0, len) 164 | } 165 | outStream.close() 166 | inStream.close() 167 | } catch (e: java.lang.Exception) { 168 | e.printStackTrace() 169 | } 170 | } 171 | 172 | /** 173 | * custom disposer 174 | * rotation image 175 | */ 176 | class CustomDisposer : Disposer { 177 | override fun disposeFile(originPath: String, targetToSaveResult: File?): DisposeResult { 178 | return DisposeResult().also { 179 | var bitmap = QualityCompressor() 180 | .compress(originPath, 80) 181 | val m = Matrix() 182 | m.postRotate(90f) 183 | bitmap = Bitmap.createBitmap( 184 | bitmap!!, 0, 0, bitmap.width, 185 | bitmap.height, m, true 186 | ) 187 | it.savedFile = targetToSaveResult 188 | it.compressBitmap = bitmap 189 | } 190 | } 191 | 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /app/src/main/java/com/qw/soulphototaker/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.qw.soulphototaker 2 | 3 | 4 | import android.Manifest 5 | import android.content.Intent 6 | import android.os.Bundle 7 | import android.widget.Toast 8 | import com.qw.photo.CoCo 9 | import com.qw.photo.CoCoConfigs 10 | import com.qw.photo.Utils 11 | import com.qw.photo.callback.CoCoAdapter 12 | import com.qw.photo.constant.Range 13 | import com.qw.photo.constant.Type 14 | import com.qw.photo.pojo.CropResult 15 | 16 | import com.qw.photo.pojo.DisposeResult 17 | import com.qw.photo.pojo.PickResult 18 | import com.qw.photo.pojo.TakeResult 19 | import com.qw.soul.permission.SoulPermission 20 | import com.qw.soul.permission.bean.Permission 21 | import com.qw.soul.permission.callbcak.CheckRequestPermissionListener 22 | import kotlinx.android.synthetic.main.activity_main.* 23 | import kotlinx.android.synthetic.main.activity_main.iv_image 24 | 25 | class MainActivity : BaseToolbarActivity() { 26 | 27 | companion object { 28 | const val TAG = "CoCoDemo" 29 | } 30 | 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | super.onCreate(savedInstanceState) 33 | setContentView(R.layout.activity_main) 34 | initViewComponent() 35 | CoCoConfigs.apply { 36 | setDebug(BuildConfig.DEBUG) 37 | configCropsResultFile(createSDCardFile().absolutePath) 38 | } 39 | } 40 | 41 | private fun initViewComponent() { 42 | btn_capture.setOnClickListener { 43 | CoCo.with(this@MainActivity) 44 | .take(createSDCardFile()) 45 | .start(object : CoCoAdapter() { 46 | 47 | override fun onSuccess(data: TakeResult) { 48 | iv_image.setImageBitmap(Utils.getBitmapFromFile(data.savedFile!!.absolutePath)) 49 | } 50 | }) 51 | } 52 | btn_capture.setOnLongClickListener { 53 | startActivity(Intent(this@MainActivity, TakePictureActivity::class.java)) 54 | true 55 | } 56 | btn_pick.apply { 57 | setOnClickListener { 58 | 59 | CoCo.with(this@MainActivity) 60 | .pick() 61 | .range(Range.PICK_CONTENT) 62 | .type(Type.JPEG) 63 | .start(object : CoCoAdapter() { 64 | override fun onSuccess(data: PickResult) { 65 | Toast.makeText( 66 | this@MainActivity, 67 | data.originUri.toString(), 68 | Toast.LENGTH_SHORT 69 | ).show() 70 | iv_image.setImageURI(data.originUri) 71 | } 72 | }) 73 | 74 | } 75 | setOnLongClickListener { 76 | startActivity(Intent(this@MainActivity, PickPictureActivity::class.java)) 77 | true 78 | } 79 | } 80 | btn_dispose.apply { 81 | setOnClickListener { 82 | 83 | CoCo.with(this@MainActivity) 84 | .take(createSDCardFile()) 85 | .then() 86 | .dispose() 87 | .start(object : CoCoAdapter() { 88 | 89 | override fun onSuccess(data: DisposeResult) { 90 | iv_image.setImageBitmap(Utils.getBitmapFromFile(data.savedFile!!.absolutePath)) 91 | } 92 | }) 93 | 94 | } 95 | setOnLongClickListener { 96 | startActivity(Intent(this@MainActivity, DisposeActivity::class.java)) 97 | true 98 | } 99 | } 100 | 101 | btn_crop.apply { 102 | setOnClickListener { 103 | //request permission 104 | SoulPermission.getInstance() 105 | .checkAndRequestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, 106 | object : CheckRequestPermissionListener { 107 | override fun onPermissionOk(permission: Permission?) { 108 | 109 | CoCo.with(this@MainActivity) 110 | .take(createSDCardFile()) 111 | .then() 112 | .crop() 113 | .start(object : CoCoAdapter() { 114 | 115 | override fun onSuccess(data: CropResult) { 116 | iv_image.setImageBitmap(data.cropBitmap) 117 | } 118 | }) 119 | } 120 | 121 | override fun onPermissionDenied(permission: Permission?) { 122 | Toast.makeText( 123 | applicationContext, 124 | "need file permission first", 125 | Toast.LENGTH_SHORT 126 | ).show() 127 | } 128 | }) 129 | 130 | } 131 | setOnLongClickListener { 132 | startActivity(Intent(this@MainActivity, CropActivity::class.java)) 133 | true 134 | } 135 | } 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/qw/soulphototaker/PickPictureActivity.kt: -------------------------------------------------------------------------------- 1 | package com.qw.soulphototaker 2 | 3 | import android.os.Bundle 4 | import android.os.Handler 5 | import android.util.Log 6 | import android.widget.Toast 7 | import com.qw.photo.CoCo 8 | import com.qw.photo.callback.CoCoCallBack 9 | import com.qw.photo.callback.PickCallBack 10 | import com.qw.photo.constant.Range 11 | import com.qw.photo.pojo.CropResult 12 | import com.qw.photo.pojo.DisposeResult 13 | import com.qw.photo.pojo.PickResult 14 | import com.qw.soul.permission.SoulPermission 15 | import com.qw.soul.permission.bean.Permission 16 | import com.qw.soul.permission.callbcak.CheckRequestPermissionListener 17 | import kotlinx.android.synthetic.main.activity_funtion_detail.iv_image 18 | import kotlinx.android.synthetic.main.activity_take_photo.* 19 | import kotlin.system.exitProcess 20 | 21 | 22 | /** 23 | * @author cd5160866 24 | */ 25 | class PickPictureActivity : BaseToolbarActivity() { 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | setContentView(R.layout.activity_pick_photo) 30 | setSupportActionBar(toolbar) 31 | title = "PickPhotoDetail" 32 | 33 | //base usage 34 | base.setOnClickListener { 35 | CoCo.with(this@PickPictureActivity) 36 | .pick() 37 | .start(object : CoCoCallBack { 38 | 39 | override fun onSuccess(data: PickResult) { 40 | iv_image.setImageURI(data.originUri) 41 | } 42 | 43 | override fun onFailed(exception: Exception) {} 44 | }) 45 | } 46 | 47 | //other functions in take operate 48 | others.setOnClickListener { 49 | CoCo.with(this@PickPictureActivity) 50 | .pick() 51 | .range(Range.PICK_CONTENT) 52 | // .range(Range.PICK_DICM) 53 | .callBack(object : PickCallBack { 54 | 55 | override fun onFinish(result: PickResult) { 56 | Log.d(MainActivity.TAG, "take onFinish${result}") 57 | } 58 | 59 | override fun onCancel() { 60 | Log.d(MainActivity.TAG, "take onCancel") 61 | } 62 | 63 | override fun onStart() { 64 | Log.d(MainActivity.TAG, "take onStart") 65 | } 66 | 67 | }).start(object : CoCoCallBack { 68 | 69 | override fun onSuccess(data: PickResult) { 70 | iv_image.setImageURI(data.originUri) 71 | } 72 | 73 | override fun onFailed(exception: Exception) {} 74 | }) 75 | } 76 | 77 | //work with other operate 78 | //pick photo first then dispose image to makes result smaller 79 | comb.setOnClickListener { 80 | //dispose need operate IO APi, so if you dispose sd card file you will need storage permission 81 | //https://github.com/soulqw/SoulPermission 82 | SoulPermission.getInstance() 83 | .checkAndRequestPermission("android.permission.READ_EXTERNAL_STORAGE", object : 84 | CheckRequestPermissionListener { 85 | 86 | override fun onPermissionOk(permission: Permission?) { 87 | pickAndDisposeImage() 88 | } 89 | 90 | override fun onPermissionDenied(permission: Permission?) { 91 | Toast.makeText( 92 | this@PickPictureActivity, 93 | "need permission first to pick Photo", 94 | Toast.LENGTH_SHORT 95 | ).show() 96 | } 97 | 98 | }) 99 | } 100 | } 101 | 102 | fun pickAndDisposeImage() { 103 | CoCo.with(this@PickPictureActivity) 104 | .pick() 105 | .then() 106 | .dispose() 107 | .fileToSaveResult(createSDCardFile()) 108 | .start(object : CoCoCallBack { 109 | 110 | override fun onSuccess(data: DisposeResult) { 111 | iv_image.setImageBitmap(data.compressBitmap) 112 | } 113 | 114 | override fun onFailed(exception: Exception) { 115 | } 116 | }) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/com/qw/soulphototaker/TakePictureActivity.kt: -------------------------------------------------------------------------------- 1 | package com.qw.soulphototaker 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import com.qw.photo.CoCo 6 | import com.qw.photo.Utils 7 | import com.qw.photo.callback.CoCoCallBack 8 | import com.qw.photo.callback.TakeCallBack 9 | import com.qw.photo.constant.Face 10 | import com.qw.photo.functions.TakeBuilder 11 | import com.qw.photo.pojo.CropResult 12 | import com.qw.photo.pojo.DisposeResult 13 | import com.qw.photo.pojo.TakeResult 14 | import kotlinx.android.synthetic.main.activity_take_photo.* 15 | 16 | /** 17 | * @author cd5160866 18 | */ 19 | class TakePictureActivity : BaseToolbarActivity() { 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | setContentView(R.layout.activity_take_photo) 24 | setSupportActionBar(toolbar) 25 | title = "TakePhotoDetail" 26 | 27 | //base usage 28 | base.setOnClickListener { 29 | CoCo.with(this@TakePictureActivity) 30 | .take(createSDCardFile()) 31 | .start(object : CoCoCallBack { 32 | 33 | override fun onSuccess(data: TakeResult) { 34 | iv_image.setImageBitmap(Utils.getBitmapFromFile(data.savedFile!!.absolutePath)) 35 | } 36 | 37 | override fun onFailed(exception: Exception) {} 38 | }) 39 | } 40 | 41 | //other functions in take operate 42 | others.setOnClickListener { 43 | CoCo.with(this@TakePictureActivity) 44 | .take(createSDCardFile()) 45 | .cameraFace(Face.FRONT) 46 | .callBack(object : TakeCallBack { 47 | 48 | override fun onFinish(result: TakeResult) { 49 | Log.d(MainActivity.TAG, "take onFinish${result}") 50 | } 51 | 52 | override fun onCancel() { 53 | Log.d(MainActivity.TAG, "take onCancel") 54 | } 55 | 56 | override fun onStart() { 57 | Log.d(MainActivity.TAG, "take onStart") 58 | } 59 | 60 | }).start(object : CoCoCallBack { 61 | 62 | override fun onSuccess(data: TakeResult) { 63 | iv_image.setImageBitmap(Utils.getBitmapFromFile(data.savedFile!!.absolutePath)) 64 | } 65 | 66 | override fun onFailed(exception: Exception) {} 67 | }) 68 | } 69 | 70 | //work with other operate 71 | //take photo first then dispose image to makes result smaller 72 | comb.setOnClickListener { 73 | CoCo.with(this@TakePictureActivity) 74 | .take(createSDCardFile()) 75 | .then() 76 | .dispose() 77 | .start(object : CoCoCallBack { 78 | 79 | override fun onSuccess(data: DisposeResult) { 80 | iv_image.setImageBitmap(data.compressBitmap) 81 | } 82 | 83 | override fun onFailed(exception: Exception) { 84 | } 85 | 86 | }) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_pill_white_hollow.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_crop_photo.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 19 | 20 |