├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── dictionaries │ └── justin.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── JscTestKeyStore.jks ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── jsc │ │ └── exam │ │ └── com │ │ └── cameramask │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── jsc │ │ │ └── exam │ │ │ └── com │ │ │ └── cameramask │ │ │ ├── BaseActivity.java │ │ │ ├── BaseAppCompatActivity.java │ │ │ ├── BaseEmptyFragmentActivity.java │ │ │ ├── EmptyFragmentActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── adapter │ │ │ ├── BaseRecyclerViewAdapter.java │ │ │ ├── BlankSpaceItemDecoration.java │ │ │ ├── ClassItemAdapter.java │ │ │ └── ViewAdapter.java │ │ │ ├── bean │ │ │ └── ClassItem.java │ │ │ ├── fragments │ │ │ ├── AboutFragment.java │ │ │ ├── CameraLensViewFragment.java │ │ │ ├── CameraScannerMaskViewFragment.java │ │ │ └── ScannerBarViewFragment.java │ │ │ ├── retrofit │ │ │ ├── ApiService.java │ │ │ ├── CustomHttpClient.java │ │ │ ├── CustomRetrofit.java │ │ │ └── LoadingDialogObserver.java │ │ │ ├── utils │ │ │ ├── CompatResourceUtils.java │ │ │ ├── ConnectivityHelper.java │ │ │ └── WindowUtils.java │ │ │ └── widgets │ │ │ ├── DotView.java │ │ │ ├── JSCItemLayout.java │ │ │ ├── SquareImageView.java │ │ │ └── dialog │ │ │ └── BottomShowDialog.java │ └── res │ │ ├── drawable-v21 │ │ └── ripple_round_corner_white_r4.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xxhdpi │ │ ├── btn_background.png │ │ ├── default_camera_lens.png │ │ ├── ic_chevron_left_white_24dp.png │ │ ├── kit_ic_assignment_blue_24dp.png │ │ └── kit_ic_chevron_right_gray_24dp.png │ │ ├── drawable │ │ ├── beauty_bg.jpg │ │ ├── camera_mask_demo_qr_code.png │ │ ├── ic_launcher_background.xml │ │ └── ripple_round_corner_white_r4.xml │ │ ├── layout │ │ ├── fragment_abount.xml │ │ ├── fragment_camera_lens_view.xml │ │ ├── fragment_camera_scanner_mask_view.xml │ │ └── fragment_scanner_bar_view.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── jsc │ └── exam │ └── com │ └── cameramask │ └── ExampleUnitTest.java ├── build.gradle ├── cameraMaskLibrary ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── jsc │ │ └── kit │ │ └── cameramask │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── jsc │ │ │ └── kit │ │ │ └── cameramask │ │ │ ├── CameraLensView.java │ │ │ ├── CameraScannerMaskView.java │ │ │ └── ScannerBarView.java │ └── res │ │ ├── drawable │ │ ├── camera_mask_scanner_bar.png │ │ └── camera_mask_scanner_line.png │ │ └── values │ │ └── camera_mask_attrs.xml │ └── test │ └── java │ └── jsc │ └── kit │ └── cameramask │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── output ├── CameraMaskDemo.apk ├── output.json └── shots │ ├── camera_lens_view_bitmap.png │ ├── camera_lens_view_bitmap_s.png │ ├── camera_lens_view_circle.png │ ├── camera_lens_view_circle_s.png │ ├── camera_lens_view_pic.png │ ├── camera_lens_view_pic_s.png │ ├── camera_lens_view_square.png │ ├── camera_lens_view_square_s.png │ ├── camera_scanner_mask_view.png │ ├── camera_scanner_mask_view_s.png │ ├── scanner_bar_view.png │ └── scanner_bar_view_s.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/dictionaries/justin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 94 | 106 | 107 | 108 | 109 | 110 | 111 | 113 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /JscTestKeyStore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinRoom/CameraMaskDemo/f9fa5ca8346c4f8412a494239e0a7c39dfd5742f/JscTestKeyStore.jks -------------------------------------------------------------------------------- /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 2018 JustinRoom 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 | # CameraMask 2 | **LatestVersion** 3 | 4 | [ ![Download](https://api.bintray.com/packages/justinquote/maven/camera-mask/images/download.svg) ](https://bintray.com/justinquote/maven/camera-mask/_latestVersion) 5 | 6 | 7 | 8 | camera mask library and demo 9 | 10 | 11 | Scan QRCode to download demo application below: 12 | 13 | ![](/app/src/main/res/drawable/camera_mask_demo_qr_code.png) 14 | 15 | ### 1、implementation 16 | + 1.1、Gradle 17 | ``` 18 | compile 'jsc.kit.cameramask:camera-mask:_latestVersion' 19 | ``` 20 | + 1.2、Maven 21 | ``` 22 | 23 | jsc.kit.cameramask 24 | camera-mask 25 | _latestVersion 26 | pom 27 | 28 | ``` 29 | 30 | ### 2、attrs 31 | + 2.1、[CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java) 32 | 33 | | 名称 | 类型 | 描述 | 34 | |:---|:---|:---| 35 | |`clvCameraLensSizeRatio`|float|相机镜头(或扫描框)大小占View宽度的百分比| 36 | |`clvCameraLensWidthWeight`|string,例如:`{1.5,4}`|相机镜头(或扫描框)宽度比重| 37 | |`clvCameraLensHeightWeight`|string,例如:`{1.5,4}`|相机镜头(或扫描框)高度比重| 38 | |`clvCameraLensWidth`|dimension|相机镜头(或扫描框)宽度| 39 | |`clvCameraLensHeight`|dimension|相机镜头(或扫描框)高度| 40 | |`clvCameraLensGravity`|enum(`top`、`center`、`bottom`)|相机镜头(或扫描框)位置| 41 | |`clvCameraLensTopMargin`|dimension|相机镜头(或扫描框)Y轴偏移量| 42 | |`clvCameraLensShape`|enum(`rectangle`、`circular`)|相机镜头(或扫描框)形状| 43 | |`clvCameraLens`|reference|相机镜头图片资源| 44 | |`clvMaskColor`|color|相机镜头遮罩颜色| 45 | |`clvBoxBorderColor`|color|扫描框边的颜色| 46 | |`clvBoxBorderWidth`|dimension|扫描框边的粗细| 47 | |`clvShowBoxAngle`|boolean|是否显示扫描框四个角| 48 | |`clvBoxAngleColor`|color|扫描框四个角的颜色| 49 | |`clvBoxAngleBorderWidth`|dimension|扫描框四个角边的粗细| 50 | |`clvBoxAngleLength`|dimension|扫描框四个角边的长度| 51 | |`clvText`|string|提示文字| 52 | |`clvTextColor`|color|提示文字颜色| 53 | |`clvTextSize`|dimension|提示文字字体大小| 54 | |`clvTextMathParent`|boolean|提示文字是否填充View的宽度。true与View等宽,false与相机镜头(或扫描框)等宽。| 55 | |`clvTextLocation`|enum(`belowCameraLens`、`aboveCameraLens`)|提示文字位于相机镜头(或扫描框)上方(或下方)| 56 | |`clvTextVerticalMargin`|dimension|提示文字与相机镜头(或扫描框)的间距| 57 | |`clvTextLeftMargin`|dimension|提示文字与View(或相机镜头或扫描框)的左间距| 58 | |`clvTextRightMargin`|dimension|提示文字与View(或相机镜头或扫描框)的右间距| 59 | 60 | + 2.2、[ScannerBarView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/ScannerBarView.java) 61 | 62 | | 名称 | 类型 | 描述 | 63 | |:---|:---|:---| 64 | |`sbvSrc`|reference|扫描条图片| 65 | 66 | 67 | + 2.3、[CameraScannerMaskView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraScannerMaskView.java) 68 | 69 | | 子View | 类型 | 属性 | 70 | |:---|:---|:---| 71 | |`cameraLensView`|[CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java)|[CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java)所有属性| 72 | |`scannerBarView`|[ScannerBarView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/ScannerBarView.java)|[ScannerBarView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/ScannerBarView.java)所有属性| 73 | 74 | ### 3、usage 75 | + 3.1、[CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java): 76 | `{1.5,4}`好比LinearLayout的`weightSum`=4, 其中相机镜头所占比重为1.5 77 | ``` 78 | 95 | ``` 96 | 97 | + 3.2、[ScannerBarView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/ScannerBarView.java): 98 | ``` 99 | 107 | ``` 108 | 109 | + 3.3、[CameraScannerMaskView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraScannerMaskView.java): 110 | ``` 111 | 121 | ``` 122 | 123 | | 组件 | 使用示例 | 124 | |:---|:---| 125 | |[ScannerBarView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/ScannerBarView.java)|[ScannerBarViewFragment](/app/src/main/java/jsc/exam/com/cameramask/fragments/ScannerBarViewFragment.java)| 126 | |[CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java)|[CameraLensViewFragment](/app/src/main/java/jsc/exam/com/cameramask/fragments/CameraLensViewFragment.java)| 127 | |[CameraScannerMaskView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraScannerMaskView.java)|[CameraScannerMaskViewFragment](/app/src/main/java/jsc/exam/com/cameramask/fragments/CameraScannerMaskViewFragment.java)| 128 | 129 | ### 4、Screenshots 130 | + 4.1、[ScannerBarView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/ScannerBarView.java) 131 | 132 | ![ScannerBarView](/output/shots/scanner_bar_view_s.png) 133 | 134 | + 4.2、[CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java) 135 | 136 | ![CameraLensView:picture](/output/shots/camera_lens_view_pic_s.png) 137 | ![CameraLensView:circle-shape](/output/shots/camera_lens_view_circle_s.png) 138 | ![CameraLensView:square-shape](/output/shots/camera_lens_view_square_s.png) 139 | ![CameraLensView:crop-bitmap](/output/shots/camera_lens_view_bitmap_s.png) 140 | 141 | + 4.3、[CameraScannerMaskView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraScannerMaskView.java) 142 | 143 | ![CameraScannerMaskView](/output/shots/camera_scanner_mask_view_s.png) 144 | 145 | ### 5、release log 146 | 147 | ##### version:0.3.0 148 | + 1、fix bugs 149 | + 2、optimize CameraLensView, add attrs: 150 | clvCameraLensWidthWeight:相机镜头宽度比重,例如:{1.5,4} 151 | clvCameraLensHeightWeight:相机镜头高度比重,例如:`{5,2} 152 | 153 | ##### version:0.2.1 154 | + 1、optimize [CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java), add attrs: 155 | clvCameraLensWidth:相机镜头(或扫描框)宽度 156 | clvCameraLensHeight:相机镜头(或扫描框)高 157 | clvCameraLensGravity(`top`、`center`、`bottom`):相机镜头(或扫描框)位置 158 | clvShowBoxAngle:是否显示扫描框四个角 159 | 160 | 161 | ##### version:0.1.2 162 | + 1、optimize [CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java) 163 | 164 | ##### version:0.1.1 165 | + 1、add method in [CameraLensView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraLensView.java)、[CameraScannerMaskView](/cameraMaskLibrary/src/main/java/jsc/kit/cameramask/CameraScannerMaskView.java): 166 | `Bitmap cropCameraLensRectBitmap(Bitmap src, boolean withRatio)` 167 | 168 | ### LICENSE 169 | ``` 170 | Copyright 2018 JustinRoom 171 | 172 | Licensed under the Apache License, Version 2.0 (the "License"); 173 | you may not use this file except in compliance with the License. 174 | You may obtain a copy of the License at 175 | 176 | http://www.apache.org/licenses/LICENSE-2.0 177 | 178 | Unless required by applicable law or agreed to in writing, software 179 | distributed under the License is distributed on an "AS IS" BASIS, 180 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 181 | See the License for the specific language governing permissions and 182 | limitations under the License. 183 | ``` 184 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | import java.text.SimpleDateFormat 2 | 3 | apply plugin: 'com.android.application' 4 | def apkName = 'CameraMaskDemo.apk' 5 | 6 | android { 7 | compileSdkVersion sdk_veriosn 8 | defaultConfig { 9 | applicationId "jsc.exam.com.cameramask" 10 | minSdkVersion min_sdk_veriosn 11 | targetSdkVersion sdk_veriosn 12 | versionCode version_code 13 | versionName version_name 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | buildConfigField("String", "BUILD_TIME", "\"${new SimpleDateFormat("yyyy年MM月dd日 HH:mm").format(new Date())}\"") 17 | buildConfigField("String", "BASE_URL", "\"https://raw.githubusercontent.com/\"") 18 | buildConfigField("String", "VERSION_URL", "\"JustinRoom/${rootProject.name}/master/output/output.json\"") 19 | buildConfigField("String", "DOWNLOAD_URL", "\"JustinRoom/${rootProject.name}/master/output/%s\"") 20 | buildConfigField("String", "GITHUB_URL", "\"https://github.com/JustinRoom/${rootProject.name}\"") 21 | buildConfigField("String", "APK_URL", "\"https://raw.githubusercontent.com/JustinRoom/${rootProject.name}/master/output/${apkName}\"") 22 | } 23 | 24 | signingConfigs { 25 | debug { 26 | keyAlias 'Jsc' 27 | keyPassword '123456' 28 | storeFile file('../JscTestKeyStore.jks') 29 | storePassword '123456' 30 | } 31 | release { 32 | keyAlias 'Jsc' 33 | keyPassword '123456' 34 | storeFile file('../JscTestKeyStore.jks') 35 | storePassword '123456' 36 | } 37 | } 38 | 39 | buildTypes { 40 | debug { 41 | signingConfig signingConfigs.debug 42 | } 43 | release { 44 | minifyEnabled false 45 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 46 | signingConfig signingConfigs.release 47 | } 48 | } 49 | applicationVariants.all { variant -> 50 | variant.outputs.all { 51 | outputFileName = "${apkName}" 52 | } 53 | } 54 | } 55 | 56 | dependencies { 57 | implementation fileTree(include: ['*.jar'], dir: 'libs') 58 | implementation 'com.android.support:appcompat-v7:28.0.0' 59 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 60 | testImplementation 'junit:junit:4.12' 61 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 62 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 63 | implementation project(':cameraMaskLibrary') 64 | implementation 'com.android.support:recyclerview-v7:28.0.0' 65 | 66 | api 'com.squareup.retrofit2:retrofit:2.4.0' 67 | api 'com.squareup.retrofit2:converter-gson:2.4.0' 68 | api 'com.squareup.retrofit2:converter-scalars:2.4.0' 69 | api 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' 70 | //https://github.com/square/okhttp 71 | api 'com.squareup.okhttp3:logging-interceptor:3.10.0' 72 | 73 | //https://github.com/ReactiveX/RxAndroid 74 | api 'io.reactivex.rxjava2:rxandroid:2.0.2' 75 | api 'io.reactivex.rxjava2:rxjava:2.1.12' 76 | } 77 | -------------------------------------------------------------------------------- /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/androidTest/java/jsc/exam/com/cameramask/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("jsc.exam.com.wheelview", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask; 2 | 3 | import android.graphics.Color; 4 | import android.support.annotation.StringRes; 5 | import android.support.v7.app.ActionBar; 6 | import android.support.v7.widget.ActionMenuView; 7 | import android.util.TypedValue; 8 | import android.view.Gravity; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.FrameLayout; 12 | import android.widget.ImageView; 13 | import android.widget.TextView; 14 | 15 | import jsc.exam.com.cameramask.utils.CompatResourceUtils; 16 | import jsc.exam.com.cameramask.utils.WindowUtils; 17 | 18 | public abstract class BaseActivity extends BaseAppCompatActivity { 19 | 20 | private ImageView ivBack; 21 | private TextView tvTitle; 22 | private ActionMenuView actionMenuView; 23 | 24 | @Override 25 | protected void initActionBar(ActionBar actionBar) { 26 | if (actionBar == null) 27 | return; 28 | 29 | int padding = CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_12); 30 | FrameLayout customView = new FrameLayout(this); 31 | // customView.setPadding(padding, 0, padding, 0); 32 | ActionBar.LayoutParams barParams = new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, WindowUtils.getActionBarSize(this)); 33 | actionBar.setDisplayShowCustomEnabled(true); 34 | actionBar.setCustomView(customView, barParams); 35 | //添加标题 36 | tvTitle = new TextView(this); 37 | tvTitle.setTextColor(Color.WHITE); 38 | tvTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18); 39 | tvTitle.setGravity(Gravity.CENTER); 40 | customView.addView(tvTitle, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); 41 | //添加返回按钮 42 | ivBack = new ImageView(this); 43 | ivBack.setPadding(padding / 2, 0, padding / 2, 0); 44 | ivBack.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 45 | ivBack.setImageResource(R.drawable.ic_chevron_left_white_24dp); 46 | ivBack.setBackground(WindowUtils.getSelectableItemBackgroundBorderless(this)); 47 | customView.addView(ivBack, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)); 48 | ivBack.setOnClickListener(new View.OnClickListener() { 49 | @Override 50 | public void onClick(View v) { 51 | onBackPressed(); 52 | } 53 | }); 54 | //添加menu菜单 55 | actionMenuView = new ActionMenuView(this); 56 | FrameLayout.LayoutParams menuParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); 57 | menuParams.gravity = Gravity.END | Gravity.CENTER_VERTICAL; 58 | customView.addView(actionMenuView, menuParams); 59 | } 60 | 61 | public ActionMenuView getActionMenuView() { 62 | return actionMenuView; 63 | } 64 | 65 | public final void showTitleBarBackView(boolean show) { 66 | if (ivBack != null) 67 | ivBack.setVisibility(show ? View.VISIBLE : View.GONE); 68 | } 69 | 70 | public final void setTitleBarTitle(CharSequence title) { 71 | if (tvTitle != null) 72 | tvTitle.setText(title); 73 | } 74 | 75 | public final void setTitleBarTitle(@StringRes int resId) { 76 | if (tvTitle != null) 77 | tvTitle.setText(resId); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/BaseAppCompatActivity.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.CallSuper; 5 | import android.support.annotation.Nullable; 6 | import android.support.annotation.StringRes; 7 | import android.support.v7.app.ActionBar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.Window; 10 | import android.view.WindowManager; 11 | import android.widget.Toast; 12 | 13 | /** 14 | *
Email:1006368252@qq.com 15 | *
QQ:1006368252 16 | *
https://github.com/JustinRoom/CameraMaskDemo 17 | * 18 | * @author jiangshicheng 19 | */ 20 | public abstract class BaseAppCompatActivity extends AppCompatActivity { 21 | 22 | /** 23 | * Show full screen or not. 24 | * 25 | * @return {@code true}, show full screen, else not. 26 | */ 27 | protected boolean fullScreen() { 28 | return false; 29 | } 30 | 31 | protected void initActionBar(ActionBar actionBar) { 32 | 33 | } 34 | 35 | @Override 36 | protected void onCreate(@Nullable Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | initComponent(); 39 | if (fullScreen()) { 40 | //without ActionBar 41 | requestWindowFeature(Window.FEATURE_NO_TITLE); 42 | if (getSupportActionBar() != null) 43 | getSupportActionBar().hide(); 44 | //show full screen 45 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 46 | } else { 47 | initActionBar(getSupportActionBar()); 48 | } 49 | } 50 | 51 | /** 52 | * Initialize components here. 53 | */ 54 | protected void initComponent() { 55 | 56 | } 57 | 58 | /** 59 | * Destroy components here. 60 | */ 61 | @CallSuper 62 | protected void destroyComponent() { 63 | 64 | } 65 | 66 | @Override 67 | protected void onDestroy() { 68 | destroyComponent(); 69 | super.onDestroy(); 70 | } 71 | 72 | public final void showToast(@StringRes int resId) { 73 | Toast.makeText(this, resId, Toast.LENGTH_SHORT).show(); 74 | } 75 | 76 | public final void showToast(CharSequence txt) { 77 | Toast.makeText(this, txt, Toast.LENGTH_SHORT).show(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/BaseEmptyFragmentActivity.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask; 2 | 3 | import android.content.pm.ActivityInfo; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.app.ActionBar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.Window; 10 | import android.view.WindowManager; 11 | 12 | /** 13 | * Empty activity for launching any {@link Fragment}. 14 | * 15 | *
Email:1006368252@qq.com 16 | *
QQ:1006368252 17 | *
https://github.com/JustinRoom/CameraMaskDemo 18 | * 19 | * @author jiangshicheng 20 | */ 21 | public abstract class BaseEmptyFragmentActivity extends AppCompatActivity { 22 | 23 | public final static String EXTRA_FULL_SCREEN = "full_screen"; 24 | public final static String EXTRA_SHOW_ACTION_BAR = "show_action_bar"; 25 | public final static String EXTRA_FRAGMENT_CLASS_NAME = "class_name"; 26 | public final static String EXTRA_TITLE = "title"; 27 | public final static String EXTRA_LANDSCAPE = "landscape"; 28 | 29 | public abstract void initActionBar(ActionBar actionBar); 30 | 31 | @Override 32 | protected void onCreate(@Nullable Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | if (getIntent().getBooleanExtra(EXTRA_FULL_SCREEN, false)) { 35 | //without ActionBar 36 | requestWindowFeature(Window.FEATURE_NO_TITLE); 37 | if (getSupportActionBar() != null) 38 | getSupportActionBar().hide(); 39 | //show full screen 40 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); 41 | } else { 42 | if (getIntent().getBooleanExtra(EXTRA_SHOW_ACTION_BAR, true)) { 43 | initActionBar(getSupportActionBar()); 44 | } else if (getSupportActionBar() != null) { 45 | getSupportActionBar().hide(); 46 | } 47 | } 48 | 49 | String className = getIntent().getStringExtra(EXTRA_FRAGMENT_CLASS_NAME); 50 | if (className != null && className.length() > 0) { 51 | Class clzz = null; 52 | Object obj = null; 53 | try { 54 | clzz = Class.forName(className); 55 | obj = clzz.newInstance(); 56 | } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { 57 | e.printStackTrace(); 58 | } 59 | if (obj instanceof Fragment) { 60 | Fragment fragment = (Fragment) obj; 61 | Bundle arguments = getIntent().getExtras(); 62 | if (arguments != null) { 63 | fragment.setArguments(arguments); 64 | } 65 | getSupportFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit(); 66 | } 67 | } 68 | } 69 | 70 | @Override 71 | protected void onResume() { 72 | if (getIntent().getBooleanExtra(EXTRA_LANDSCAPE, false) && getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { 73 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 74 | } 75 | super.onResume(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/EmptyFragmentActivity.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Color; 6 | import android.os.Bundle; 7 | import android.support.v7.app.ActionBar; 8 | import android.support.v7.widget.ActionMenuView; 9 | import android.text.TextUtils; 10 | import android.util.TypedValue; 11 | import android.view.Gravity; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.FrameLayout; 15 | import android.widget.ImageView; 16 | import android.widget.TextView; 17 | 18 | import jsc.exam.com.cameramask.utils.CompatResourceUtils; 19 | import jsc.exam.com.cameramask.utils.WindowUtils; 20 | 21 | /** 22 | *
Email:1006368252@qq.com 23 | *
QQ:1006368252 24 | *
https://github.com/JustinRoom/CameraMaskDemo 25 | * 26 | * @author jiangshicheng 27 | */ 28 | public class EmptyFragmentActivity extends BaseEmptyFragmentActivity { 29 | 30 | public static void launch(Context context, Bundle extras) { 31 | context.startActivity(createIntent(context, extras)); 32 | } 33 | 34 | private static Intent createIntent(Context context, Bundle extras) { 35 | Intent intent = new Intent(context, EmptyFragmentActivity.class); 36 | if (extras != null) 37 | intent.putExtras(extras); 38 | return intent; 39 | } 40 | 41 | @Override 42 | public void initActionBar(ActionBar actionBar) { 43 | if (actionBar == null) 44 | return; 45 | 46 | //You can initialize custom action bar here. 47 | int padding = CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_12); 48 | FrameLayout customView = new FrameLayout(this); 49 | // customView.setPadding(padding, 0, padding, 0); 50 | ActionBar.LayoutParams barParams = new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, WindowUtils.getActionBarSize(this)); 51 | actionBar.setDisplayShowCustomEnabled(true); 52 | actionBar.setCustomView(customView, barParams); 53 | //添加标题 54 | TextView tvTitle = new TextView(this); 55 | tvTitle.setTextColor(Color.WHITE); 56 | tvTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18); 57 | tvTitle.setGravity(Gravity.CENTER); 58 | tvTitle.setText(getClass().getSimpleName().replace("Activity", "")); 59 | customView.addView(tvTitle, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); 60 | //添加返回按钮 61 | ImageView ivBack = new ImageView(this); 62 | ivBack.setPadding(padding / 2, 0, padding / 2, 0); 63 | ivBack.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 64 | ivBack.setImageResource(R.drawable.ic_chevron_left_white_24dp); 65 | ivBack.setBackground(WindowUtils.getSelectableItemBackgroundBorderless(this)); 66 | customView.addView(ivBack, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)); 67 | ivBack.setOnClickListener(new View.OnClickListener() { 68 | @Override 69 | public void onClick(View v) { 70 | onBackPressed(); 71 | } 72 | }); 73 | //添加menu菜单 74 | ActionMenuView actionMenuView = new ActionMenuView(this); 75 | FrameLayout.LayoutParams menuParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); 76 | menuParams.gravity = Gravity.END | Gravity.CENTER_VERTICAL; 77 | customView.addView(actionMenuView, menuParams); 78 | 79 | String title = getIntent().getStringExtra(EXTRA_TITLE); 80 | tvTitle.setText(TextUtils.isEmpty(title) ? getClass().getSimpleName().replace("Activity", "") : title); 81 | } 82 | } -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/MainActivity.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.content.pm.PackageInfo; 7 | import android.content.pm.PackageManager; 8 | import android.net.Uri; 9 | import android.os.Bundle; 10 | import android.support.v7.app.AlertDialog; 11 | import android.support.v7.widget.LinearLayoutManager; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.util.Log; 14 | import android.util.Pair; 15 | import android.view.View; 16 | import android.widget.FrameLayout; 17 | 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Date; 23 | import java.util.List; 24 | import java.util.Locale; 25 | 26 | import io.reactivex.android.schedulers.AndroidSchedulers; 27 | import io.reactivex.disposables.Disposable; 28 | import io.reactivex.functions.Action; 29 | import io.reactivex.functions.Consumer; 30 | import io.reactivex.schedulers.Schedulers; 31 | import jsc.exam.com.cameramask.adapter.BaseRecyclerViewAdapter; 32 | import jsc.exam.com.cameramask.adapter.BlankSpaceItemDecoration; 33 | import jsc.exam.com.cameramask.adapter.ClassItemAdapter; 34 | import jsc.exam.com.cameramask.bean.ClassItem; 35 | import jsc.exam.com.cameramask.fragments.AboutFragment; 36 | import jsc.exam.com.cameramask.fragments.CameraLensViewFragment; 37 | import jsc.exam.com.cameramask.fragments.CameraScannerMaskViewFragment; 38 | import jsc.exam.com.cameramask.fragments.ScannerBarViewFragment; 39 | import jsc.exam.com.cameramask.retrofit.ApiService; 40 | import jsc.exam.com.cameramask.retrofit.CustomHttpClient; 41 | import jsc.exam.com.cameramask.retrofit.CustomRetrofit; 42 | import jsc.exam.com.cameramask.utils.CompatResourceUtils; 43 | import okhttp3.OkHttpClient; 44 | import retrofit2.Retrofit; 45 | 46 | public class MainActivity extends BaseActivity { 47 | 48 | RecyclerView recyclerView; 49 | SharedPreferences sharedPreferences = null; 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | recyclerView = new RecyclerView(this); 55 | recyclerView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); 56 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 57 | recyclerView.addItemDecoration(new BlankSpaceItemDecoration( 58 | CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_16), 59 | CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_2), 60 | CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_16), 61 | CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_2) 62 | )); 63 | setContentView(recyclerView); 64 | setTitleBarTitle(getClass().getSimpleName().replace("Activity", "")); 65 | showTitleBarBackView(false); 66 | 67 | ClassItemAdapter adapter = new ClassItemAdapter(); 68 | recyclerView.setAdapter(adapter); 69 | adapter.setOnItemClickListener(new BaseRecyclerViewAdapter.OnItemClickListener() { 70 | @Override 71 | public void onItemClick(View itemView, int position, ClassItem item, int viewType) { 72 | toNewActivity(item); 73 | } 74 | }); 75 | adapter.setItems(getClassItems()); 76 | 77 | //check upgrade if the latest checking time is 2 hours ago 78 | sharedPreferences = getSharedPreferences("share_camera_mask_view", MODE_PRIVATE); 79 | long lastCheckUpdateTimeStamp = sharedPreferences.getLong("lastCheckUpdateTimeStamp", 0); 80 | long curTime = new Date().getTime(); 81 | if (curTime - lastCheckUpdateTimeStamp > 2 * 60 * 60_000) { 82 | checkUpdate(); 83 | } 84 | } 85 | 86 | private void toNewActivity(ClassItem item) { 87 | switch (item.getType()) { 88 | case ClassItem.TYPE_ACTIVITY: 89 | startActivity(new Intent(this, item.getClazz())); 90 | break; 91 | case ClassItem.TYPE_FRAGMENT: 92 | Bundle bundle = new Bundle(); 93 | bundle.putString(EmptyFragmentActivity.EXTRA_TITLE, item.getLabel()); 94 | bundle.putBoolean(EmptyFragmentActivity.EXTRA_FULL_SCREEN, item.isFullScreen()); 95 | bundle.putBoolean(EmptyFragmentActivity.EXTRA_SHOW_ACTION_BAR, true); 96 | bundle.putBoolean(EmptyFragmentActivity.EXTRA_LANDSCAPE, item.isLandscape()); 97 | bundle.putString(EmptyFragmentActivity.EXTRA_FRAGMENT_CLASS_NAME, item.getClazz().getName()); 98 | EmptyFragmentActivity.launch(this, bundle); 99 | break; 100 | } 101 | } 102 | 103 | private List getClassItems() { 104 | List classItems = new ArrayList<>(); 105 | classItems.add(new ClassItem(ClassItem.TYPE_FRAGMENT, "CameraLensView", CameraLensViewFragment.class, true, false, true)); 106 | classItems.add(new ClassItem(ClassItem.TYPE_FRAGMENT, "ScannerBarView", ScannerBarViewFragment.class, true, false, false)); 107 | classItems.add(new ClassItem(ClassItem.TYPE_FRAGMENT, "CameraScannerMaskView", CameraScannerMaskViewFragment.class, true, false, true)); 108 | classItems.add(new ClassItem(ClassItem.TYPE_FRAGMENT, "About", AboutFragment.class, false)); 109 | return classItems; 110 | } 111 | 112 | private void checkUpdate() { 113 | OkHttpClient client = new CustomHttpClient() 114 | .addHeader(new Pair<>("token", "")) 115 | .setConnectTimeout(5_000) 116 | .setShowLog(true) 117 | .createOkHttpClient(); 118 | Retrofit retrofit = new CustomRetrofit() 119 | //我在app的build.gradle文件的defaultConfig标签里定义了BASE_URL 120 | .setBaseUrl(BuildConfig.BASE_URL) 121 | .setOkHttpClient(client) 122 | .createRetrofit(); 123 | Disposable disposable = retrofit.create(ApiService.class) 124 | .getVersionInfo() 125 | .subscribeOn(Schedulers.io()) 126 | .observeOn(AndroidSchedulers.mainThread()) 127 | .subscribe(new Consumer() { 128 | @Override 129 | public void accept(String s) throws Exception { 130 | explainVersionInfoJson(s); 131 | } 132 | }, new Consumer() { 133 | @Override 134 | public void accept(Throwable throwable) throws Exception { 135 | showToast(throwable.getLocalizedMessage()); 136 | } 137 | }, new Action() { 138 | @Override 139 | public void run() throws Exception { 140 | 141 | } 142 | }); 143 | } 144 | 145 | private void explainVersionInfoJson(String json) { 146 | json = json.substring(1, json.length() - 1); 147 | try { 148 | JSONObject object = new JSONObject(json).getJSONObject("apkInfo"); 149 | int versionCode = object.getInt("versionCode"); 150 | String versionName = object.getString("versionName"); 151 | String fileName = object.getString("outputFile"); 152 | String content = object.getString("content"); 153 | 154 | PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_GIDS); 155 | long curVersionCode = packageInfo.versionCode; 156 | String curVersionName = packageInfo.versionName; 157 | 158 | Log.i("MainActivity", "explainVersionInfoJson: {versionCod" + versionCode + ", curVersionCode:" + curVersionCode); 159 | //a new version 160 | if (versionCode > curVersionCode) { 161 | sharedPreferences.edit().putLong("lastCheckUpdateTimeStamp", new Date().getTime()).apply(); 162 | showNewVersionDialog(String.format( 163 | Locale.CHINA, 164 | "当前版本:\u2000%1s\n" 165 | + "最新版本:\u2000%2s\n\n" 166 | + "更新内容:%3s" 167 | + "\n\n立即更新?", 168 | curVersionName, 169 | versionName, 170 | content 171 | ), fileName); 172 | } 173 | } catch (JSONException | PackageManager.NameNotFoundException e) { 174 | e.printStackTrace(); 175 | } 176 | } 177 | 178 | private void showNewVersionDialog(String content, final String fileName) { 179 | new AlertDialog.Builder(this) 180 | .setTitle("新版本提示") 181 | .setMessage(content) 182 | .setPositiveButton("更新", new DialogInterface.OnClickListener() { 183 | @Override 184 | public void onClick(DialogInterface dialog, int which) { 185 | String url = BuildConfig.BASE_URL + BuildConfig.DOWNLOAD_URL; 186 | Uri uri = Uri.parse(String.format(Locale.CHINA, url, fileName)); 187 | Intent intent = new Intent(Intent.ACTION_VIEW, uri); 188 | startActivity(intent); 189 | 190 | } 191 | }) 192 | .setNegativeButton("知道了", null) 193 | .show(); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/adapter/BaseRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.adapter; 2 | 3 | 4 | import android.support.annotation.IdRes; 5 | import android.support.annotation.IntRange; 6 | import android.support.annotation.LayoutRes; 7 | import android.support.annotation.NonNull; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | *
Email:1006368252@qq.com 18 | *
QQ:1006368252 19 | *
https://github.com/JustinRoom/CameraMaskDemo 20 | * 21 | * @author jiangshicheng 22 | */ 23 | public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter implements ViewAdapter { 24 | 25 | private List items = null; 26 | @LayoutRes 27 | protected int layoutId = -1; 28 | private boolean itemClickEnable; 29 | private boolean itemLongClickEnable; 30 | private OnItemClickListener onViewClickListener = null; 31 | private OnItemClickListener onItemClickListener = null; 32 | private OnItemLongClickListener onItemLongClickListener = null; 33 | private View.OnClickListener listener = null; 34 | private View.OnClickListener onClickListener = null; 35 | private View.OnLongClickListener onLongClickListener = null; 36 | 37 | public interface OnItemClickListener { 38 | void onItemClick(View itemView, int position, T item, int viewType); 39 | } 40 | 41 | public interface OnItemLongClickListener { 42 | boolean onItemLongClick(View itemView, int position, T item, int viewType); 43 | } 44 | 45 | public BaseRecyclerViewAdapter() { 46 | this(-1, true, true); 47 | } 48 | 49 | public BaseRecyclerViewAdapter(@LayoutRes int layoutId) { 50 | this(layoutId, true, true); 51 | } 52 | 53 | public BaseRecyclerViewAdapter(@LayoutRes int layoutId, boolean itemClickEnable, boolean itemLongClickEnable) { 54 | this.layoutId = layoutId; 55 | this.itemClickEnable = itemClickEnable; 56 | this.itemLongClickEnable = itemLongClickEnable; 57 | items = new ArrayList<>(); 58 | } 59 | 60 | public List getItems() { 61 | return items; 62 | } 63 | 64 | public T getItemAt(int position) { 65 | return position < getItemCount() ? items.get(position) : null; 66 | } 67 | 68 | public void setItems(List items) { 69 | this.items = items; 70 | ensureListNotNull(); 71 | notifyDataSetChanged(); 72 | } 73 | 74 | public void addItems(List items) { 75 | addItems(getItemCount(), items); 76 | } 77 | 78 | @Override 79 | public void addItems(@IntRange(from = 0) int position, List items) { 80 | if (items == null || items.isEmpty()) 81 | return; 82 | ensureListNotNull(); 83 | this.items.addAll(items); 84 | notifyItemRangeInserted(position, items.size()); 85 | } 86 | 87 | @Override 88 | public void addItem(T item) { 89 | addItem(getItemCount(), item); 90 | } 91 | 92 | @Override 93 | public void addItem(int position, T item) { 94 | if (item == null) 95 | return; 96 | ensureListNotNull(); 97 | this.items.add(position, item); 98 | notifyItemInserted(position); 99 | } 100 | 101 | @Override 102 | public void removeItem(T item) { 103 | if (item == null) 104 | return; 105 | int position = -1; 106 | for (int i = 0; i < getItemCount(); i++) { 107 | if (item == getItemAt(i)){ 108 | position = i; 109 | break; 110 | } 111 | } 112 | removeItem(position); 113 | } 114 | 115 | @Override 116 | public void removeItem(int position) { 117 | if (position < 0) 118 | return; 119 | items.remove(position); 120 | notifyItemRemoved(position); 121 | } 122 | 123 | private void ensureListNotNull(){ 124 | if (this.items == null) 125 | this.items = new ArrayList<>(); 126 | } 127 | 128 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 129 | this.onItemClickListener = onItemClickListener; 130 | } 131 | 132 | public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) { 133 | this.onItemLongClickListener = onItemLongClickListener; 134 | } 135 | 136 | public void setOnViewClickListener(OnItemClickListener onViewClickListener) { 137 | this.onViewClickListener = onViewClickListener; 138 | } 139 | 140 | public View createView(@NonNull ViewGroup parent, int viewType) { 141 | return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); 142 | } 143 | 144 | @Override 145 | public final void onBindViewHolder(@NonNull final VH holder, int position) { 146 | holder.itemView.setTag(position); 147 | addOnItemClickListener(holder); 148 | addOnItemLongClickListener(holder); 149 | bindViewHolder(holder, position, getItemAt(position), getItemViewType(position)); 150 | } 151 | 152 | public abstract void bindViewHolder(@NonNull final VH holder, int position, T item, int viewType); 153 | 154 | @Override 155 | public int getItemCount() { 156 | return items.size(); 157 | } 158 | 159 | private void addOnItemClickListener(@NonNull final VH holder){ 160 | if (!itemClickEnable) return; 161 | if (onClickListener == null){ 162 | onClickListener = new View.OnClickListener() { 163 | @Override 164 | public void onClick(View v) { 165 | int position = (int) v.getTag(); 166 | if (onItemClickListener != null) 167 | onItemClickListener.onItemClick(v, position, getItemAt(position), getItemViewType(position)); 168 | } 169 | }; 170 | } 171 | holder.itemView.setOnClickListener(onClickListener); 172 | } 173 | 174 | private void addOnItemLongClickListener(@NonNull final VH holder){ 175 | if (!itemLongClickEnable) return; 176 | if (onLongClickListener == null){ 177 | onLongClickListener = new View.OnLongClickListener() { 178 | @Override 179 | public boolean onLongClick(View v) { 180 | int position = (int) v.getTag(); 181 | if (onItemLongClickListener != null) { 182 | return onItemLongClickListener.onItemLongClick(v, position, getItemAt(position), getItemViewType(position)); 183 | } 184 | return false; 185 | } 186 | }; 187 | } 188 | holder.itemView.setOnLongClickListener(onLongClickListener); 189 | } 190 | 191 | public final void addOnViewClickListener(@NonNull final VH holder, @IdRes int id){ 192 | //listener使用全局模式,避免每次都创建而造成内存d抖动 193 | if (listener == null) 194 | listener = new View.OnClickListener() { 195 | @Override 196 | public void onClick(View v) { 197 | int position = holder.getAdapterPosition(); 198 | if (onViewClickListener != null) 199 | onViewClickListener.onItemClick(v, position, getItemAt(position), getItemViewType(position)); 200 | } 201 | }; 202 | View view = holder.itemView.findViewById(id); 203 | if (view != null) 204 | view.setOnClickListener(listener); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/adapter/BlankSpaceItemDecoration.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.adapter; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Rect; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | 8 | /** 9 | * Black space line {@link android.support.v7.widget.RecyclerView.ItemDecoration} for {@link RecyclerView}. 10 | *

11 | *
Email:1006368252@qq.com 12 | *
QQ:1006368252 13 | *
https://github.com/JustinRoom/CameraMaskDemo 14 | * 15 | * @author jiangshicheng 16 | */ 17 | public class BlankSpaceItemDecoration extends RecyclerView.ItemDecoration { 18 | 19 | int leftSpace; 20 | int topSpace; 21 | int rightSpace; 22 | int bottomSpace; 23 | boolean showFirstTop; 24 | boolean showLastBottom; 25 | 26 | public BlankSpaceItemDecoration(int leftSpace, int topSpace, int rightSpace, int bottomSpace) { 27 | this.leftSpace = leftSpace; 28 | this.topSpace = topSpace; 29 | this.rightSpace = rightSpace; 30 | this.bottomSpace = bottomSpace; 31 | } 32 | 33 | public void showFFTLB(boolean showFirstTop, boolean showLastBottom){ 34 | this.showFirstTop = showFirstTop; 35 | this.showLastBottom = showLastBottom; 36 | } 37 | 38 | @Override 39 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 40 | super.getItemOffsets(outRect, view, parent, state); 41 | outRect.left = leftSpace; 42 | outRect.top = topSpace; 43 | outRect.right = rightSpace; 44 | outRect.bottom = bottomSpace; 45 | RecyclerView.Adapter adapter = parent.getAdapter(); 46 | if (adapter != null && adapter.getItemCount() > 1) { 47 | if (!showFirstTop && parent.getChildAdapterPosition(view) == 0) 48 | outRect.top = 0; 49 | 50 | if (!showLastBottom && parent.getChildAdapterPosition(view) == adapter.getItemCount() - 1) 51 | outRect.bottom = 0; 52 | } 53 | } 54 | 55 | @Override 56 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 57 | super.onDraw(c, parent, state); 58 | } 59 | 60 | @Override 61 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 62 | super.onDrawOver(c, parent, state); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/adapter/ClassItemAdapter.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.adapter; 2 | 3 | 4 | import android.support.annotation.NonNull; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import jsc.exam.com.cameramask.R; 10 | import jsc.exam.com.cameramask.bean.ClassItem; 11 | import jsc.exam.com.cameramask.utils.CompatResourceUtils; 12 | import jsc.exam.com.cameramask.widgets.JSCItemLayout; 13 | 14 | public class ClassItemAdapter extends BaseRecyclerViewAdapter { 15 | 16 | public ClassItemAdapter() { 17 | } 18 | 19 | public ClassItemAdapter(int layoutId) { 20 | super(layoutId); 21 | } 22 | 23 | public ClassItemAdapter(int layoutId, boolean itemClickEnable, boolean itemLongClickEnable) { 24 | super(layoutId, itemClickEnable, itemLongClickEnable); 25 | } 26 | 27 | @Override 28 | public View createView(@NonNull ViewGroup parent, int viewType) { 29 | JSCItemLayout layout = new JSCItemLayout(parent.getContext()); 30 | layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, CompatResourceUtils.getDimensionPixelSize(parent, R.dimen.item_height))); 31 | layout.setBackgroundResource(R.drawable.ripple_round_corner_white_r4); 32 | layout.setPadding( 33 | CompatResourceUtils.getDimensionPixelSize(parent, R.dimen.space_8), 34 | 0, 35 | CompatResourceUtils.getDimensionPixelSize(parent, R.dimen.space_8), 36 | 0 37 | ); 38 | layout.getLabelView().setPadding( 39 | CompatResourceUtils.getDimensionPixelSize(parent, R.dimen.space_12), 40 | 0, 41 | 0, 42 | 0 43 | ); 44 | return layout; 45 | } 46 | 47 | @NonNull 48 | @Override 49 | public ClassItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 50 | JSCItemLayout v = (JSCItemLayout) createView(parent, viewType); 51 | return new ClassItemViewHolder(v); 52 | } 53 | 54 | @Override 55 | public void bindViewHolder(@NonNull ClassItemViewHolder holder, int position, ClassItem item, int viewType) { 56 | holder.layout.setLabel(item.getLabel()); 57 | holder.layout.showDotView(item.isUpdated()); 58 | } 59 | 60 | static class ClassItemViewHolder extends RecyclerView.ViewHolder { 61 | 62 | JSCItemLayout layout; 63 | 64 | ClassItemViewHolder(JSCItemLayout layout) { 65 | super(layout); 66 | this.layout = layout; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/adapter/ViewAdapter.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.adapter; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | *
Email:1006368252@qq.com 7 | *
QQ:1006368252 8 | *
https://github.com/JustinRoom/CameraMaskDemo 9 | * 10 | * @author jiangshicheng 11 | */ 12 | public interface ViewAdapter { 13 | 14 | public List getItems(); 15 | 16 | public T getItemAt(int position); 17 | 18 | public void setItems(List items); 19 | 20 | public void addItems(List items); 21 | 22 | public void addItems(int position, List items); 23 | 24 | public void addItem(T item); 25 | 26 | public void addItem(int position, T item); 27 | 28 | public void removeItem(int position); 29 | 30 | public void removeItem(T item); 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/bean/ClassItem.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.bean; 2 | 3 | import android.support.annotation.IntDef; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | public class ClassItem { 9 | public static final int TYPE_ACTIVITY = 0; 10 | public static final int TYPE_FRAGMENT = 1; 11 | @IntDef({TYPE_ACTIVITY, TYPE_FRAGMENT}) 12 | @Retention(RetentionPolicy.SOURCE) 13 | public @interface Type { 14 | } 15 | 16 | private String label; 17 | private Class clazz; 18 | private int type; 19 | private boolean updated; 20 | private boolean isLandscape; 21 | private boolean fullScreen; 22 | 23 | public ClassItem() { 24 | } 25 | 26 | public ClassItem(@Type int type, String label, Class clazz, boolean updated) { 27 | this(type, label, clazz, updated, false, false); 28 | } 29 | public ClassItem(@Type int type, String label, Class clazz, boolean updated, boolean isLandscape, boolean fullScreen) { 30 | this.type = type; 31 | this.label = label; 32 | this.clazz = clazz; 33 | this.updated = updated; 34 | this.isLandscape = isLandscape; 35 | this.fullScreen = fullScreen; 36 | } 37 | 38 | public String getLabel() { 39 | return label; 40 | } 41 | 42 | public void setLabel(String label) { 43 | this.label = label; 44 | } 45 | 46 | public Class getClazz() { 47 | return clazz; 48 | } 49 | 50 | public void setClazz(Class clazz) { 51 | this.clazz = clazz; 52 | } 53 | 54 | public boolean isUpdated() { 55 | return updated; 56 | } 57 | 58 | public void setUpdated(boolean updated) { 59 | this.updated = updated; 60 | } 61 | 62 | public void setType(@Type int type) { 63 | this.type = type; 64 | } 65 | 66 | public int getType() { 67 | return type; 68 | } 69 | 70 | public boolean isLandscape() { 71 | return isLandscape; 72 | } 73 | 74 | public void setLandscape(boolean landscape) { 75 | isLandscape = landscape; 76 | } 77 | 78 | public boolean isFullScreen() { 79 | return fullScreen; 80 | } 81 | 82 | public void setFullScreen(boolean fullScreen) { 83 | this.fullScreen = fullScreen; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/fragments/AboutFragment.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.fragments; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.support.annotation.NonNull; 10 | import android.support.annotation.Nullable; 11 | import android.support.v4.app.Fragment; 12 | import android.support.v7.app.AlertDialog; 13 | import android.util.Log; 14 | import android.util.Pair; 15 | import android.view.LayoutInflater; 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.widget.TextView; 19 | import android.widget.Toast; 20 | 21 | import org.json.JSONException; 22 | import org.json.JSONObject; 23 | 24 | import java.util.Locale; 25 | 26 | import io.reactivex.android.schedulers.AndroidSchedulers; 27 | import io.reactivex.disposables.Disposable; 28 | import io.reactivex.functions.Action; 29 | import io.reactivex.functions.Consumer; 30 | import io.reactivex.schedulers.Schedulers; 31 | import jsc.exam.com.cameramask.BuildConfig; 32 | import jsc.exam.com.cameramask.R; 33 | import jsc.exam.com.cameramask.retrofit.ApiService; 34 | import jsc.exam.com.cameramask.retrofit.CustomHttpClient; 35 | import jsc.exam.com.cameramask.retrofit.CustomRetrofit; 36 | import okhttp3.OkHttpClient; 37 | import retrofit2.Retrofit; 38 | 39 | /** 40 | *
Email:1006368252@qq.com 41 | *
QQ:1006368252 42 | *
https://github.com/JustinRoom/CameraMaskDemo 43 | * 44 | * @author jiangshicheng 45 | */ 46 | public class AboutFragment extends Fragment { 47 | 48 | @Nullable 49 | @Override 50 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 51 | View root = inflater.inflate(R.layout.fragment_abount, container, false); 52 | TextView tvVersion = root.findViewById(R.id.tv_version); 53 | TextView tvUpdateContent = root.findViewById(R.id.tv_update_content); 54 | TextView tvBuildTime = root.findViewById(R.id.tv_build_time); 55 | try { 56 | PackageInfo packageInfo = inflater.getContext().getPackageManager().getPackageInfo(inflater.getContext().getPackageName(), PackageManager.GET_GIDS); 57 | tvVersion.setText(String.format(Locale.CHINA, "version:%s", packageInfo.versionName)); 58 | } catch (PackageManager.NameNotFoundException e) { 59 | e.printStackTrace(); 60 | } 61 | tvBuildTime.setText(String.format(Locale.CHINA, "build time:%s", BuildConfig.BUILD_TIME)); 62 | 63 | root.findViewById(R.id.btn_check_update).setOnClickListener(new View.OnClickListener() { 64 | @Override 65 | public void onClick(View v) { 66 | v.setEnabled(false); 67 | checkUpdate(); 68 | } 69 | }); 70 | tvUpdateContent.setText("当前版本更新内容:" + getString(R.string.app_update_content)); 71 | return root; 72 | } 73 | 74 | private void checkUpdate() { 75 | OkHttpClient client = new CustomHttpClient() 76 | .addHeader(new Pair<>("token", "")) 77 | .setConnectTimeout(5_000) 78 | .setShowLog(true) 79 | .createOkHttpClient(); 80 | Retrofit retrofit = new CustomRetrofit() 81 | //我在app的build.gradle文件的defaultConfig标签里定义了BASE_URL 82 | .setBaseUrl(BuildConfig.BASE_URL) 83 | .setOkHttpClient(client) 84 | .createRetrofit(); 85 | Disposable disposable = retrofit.create(ApiService.class) 86 | .getVersionInfo() 87 | .subscribeOn(Schedulers.io()) 88 | .observeOn(AndroidSchedulers.mainThread()) 89 | .subscribe(new Consumer() { 90 | @Override 91 | public void accept(String s) throws Exception { 92 | explainVersionInfoJson(s); 93 | } 94 | }, new Consumer() { 95 | @Override 96 | public void accept(Throwable throwable) throws Exception { 97 | 98 | } 99 | }, new Action() { 100 | @Override 101 | public void run() throws Exception { 102 | if (getView() != null) { 103 | getView().findViewById(R.id.btn_check_update).setEnabled(true); 104 | } 105 | } 106 | }); 107 | } 108 | 109 | private void explainVersionInfoJson(String json) { 110 | json = json.substring(1, json.length() - 1); 111 | try { 112 | JSONObject object = new JSONObject(json).getJSONObject("apkInfo"); 113 | int versionCode = object.getInt("versionCode"); 114 | String versionName = object.getString("versionName"); 115 | String fileName = object.getString("outputFile"); 116 | String content = object.getString("content"); 117 | 118 | PackageInfo packageInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), PackageManager.GET_GIDS); 119 | long curVersionCode = packageInfo.versionCode; 120 | String curVersionName = packageInfo.versionName; 121 | 122 | Log.i("MainActivity", "explainVersionInfoJson: {versionCod" + versionCode + ", curVersionCode:" + curVersionCode); 123 | //a new version 124 | if (versionCode > curVersionCode) { 125 | showNewVersionDialog(String.format( 126 | Locale.CHINA, 127 | "当前版本:\u2000%1s\n" 128 | + "最新版本:\u2000%2s\n\n" 129 | + "更新内容:%3s" 130 | + "\n\n立即更新?", 131 | curVersionName, 132 | versionName, 133 | content 134 | ), fileName); 135 | } else { 136 | Toast.makeText(getActivity(), "已是最新版本!", Toast.LENGTH_SHORT).show(); 137 | } 138 | } catch (JSONException | PackageManager.NameNotFoundException e) { 139 | e.printStackTrace(); 140 | } 141 | } 142 | 143 | private void showNewVersionDialog(String content, final String fileName) { 144 | if (getActivity() == null) 145 | return; 146 | 147 | new AlertDialog.Builder(getActivity()) 148 | .setTitle("新版本提示") 149 | .setMessage(content) 150 | .setPositiveButton("更新", new DialogInterface.OnClickListener() { 151 | @Override 152 | public void onClick(DialogInterface dialog, int which) { 153 | String url = BuildConfig.BASE_URL + BuildConfig.DOWNLOAD_URL; 154 | Uri uri = Uri.parse(String.format(Locale.CHINA, url, fileName)); 155 | Intent intent = new Intent(Intent.ACTION_VIEW, uri); 156 | startActivity(intent); 157 | 158 | } 159 | }) 160 | .setNegativeButton("知道了", null) 161 | .show(); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/fragments/CameraLensViewFragment.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.fragments; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.app.Fragment; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.ImageView; 13 | import android.widget.RadioGroup; 14 | import android.widget.SeekBar; 15 | 16 | import jsc.exam.com.cameramask.R; 17 | import jsc.exam.com.cameramask.widgets.dialog.BottomShowDialog; 18 | import jsc.kit.cameramask.CameraLensView; 19 | 20 | /** 21 | *
Email:1006368252@qq.com 22 | *
QQ:1006368252 23 | *
https://github.com/JustinRoom/CameraMaskDemo 24 | * 25 | * @author jiangshicheng 26 | */ 27 | public class CameraLensViewFragment extends Fragment { 28 | 29 | private ImageView ivBackground; 30 | private CameraLensView cameraLensView; 31 | private Bitmap defaultCameraLensBitmap = null; 32 | 33 | @Nullable 34 | @Override 35 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 36 | View root = inflater.inflate(R.layout.fragment_camera_lens_view, container, false); 37 | initView(root); 38 | return root; 39 | } 40 | 41 | private void initView(View root) { 42 | ivBackground = root.findViewById(R.id.iv_background); 43 | cameraLensView = root.findViewById(R.id.camera_lens_view); 44 | RadioGroup typeRadioGroup = root.findViewById(R.id.radio_group_type); 45 | typeRadioGroup.check(R.id.radio_type_picture); 46 | typeRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { 47 | @Override 48 | public void onCheckedChanged(RadioGroup group, int checkedId) { 49 | switch (checkedId) { 50 | case R.id.radio_type_picture: 51 | if (defaultCameraLensBitmap == null) 52 | defaultCameraLensBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.default_camera_lens); 53 | cameraLensView.setCameraLensBitmap(defaultCameraLensBitmap); 54 | break; 55 | case R.id.radio_type_normal: 56 | cameraLensView.setCameraLensBitmap(null); 57 | break; 58 | } 59 | } 60 | }); 61 | 62 | RadioGroup shapeRadioGroup = root.findViewById(R.id.radio_group_shape); 63 | shapeRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { 64 | @Override 65 | public void onCheckedChanged(RadioGroup group, int checkedId) { 66 | switch (checkedId) { 67 | case R.id.radio_shape_circle: 68 | cameraLensView.setCameraLensShape(CameraLensView.CIRCULAR); 69 | break; 70 | case R.id.radio_shape_square: 71 | cameraLensView.setCameraLensShape(CameraLensView.RECTANGLE); 72 | break; 73 | } 74 | } 75 | }); 76 | RadioGroup locationRadioGroup = root.findViewById(R.id.radio_group_location); 77 | locationRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { 78 | @Override 79 | public void onCheckedChanged(RadioGroup group, int checkedId) { 80 | switch (checkedId) { 81 | case R.id.radio_location_below: 82 | cameraLensView.setTextLocation(CameraLensView.BELOW_CAMERA_LENS); 83 | break; 84 | case R.id.radio_location_above: 85 | cameraLensView.setTextLocation(CameraLensView.ABOVE_CAMERA_LENS); 86 | break; 87 | } 88 | } 89 | }); 90 | 91 | SeekBar topMarginSeekBar = root.findViewById(R.id.seek_bar_top_margin); 92 | topMarginSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 93 | @Override 94 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 95 | cameraLensView.setCameraLensTopMargin(progress); 96 | } 97 | 98 | @Override 99 | public void onStartTrackingTouch(SeekBar seekBar) { 100 | 101 | } 102 | 103 | @Override 104 | public void onStopTrackingTouch(SeekBar seekBar) { 105 | 106 | } 107 | }); 108 | 109 | SeekBar textVerticalMarginSeekBar = root.findViewById(R.id.seek_bar_vertical_margin); 110 | textVerticalMarginSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 111 | @Override 112 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 113 | cameraLensView.setTextVerticalMargin(progress); 114 | } 115 | 116 | @Override 117 | public void onStartTrackingTouch(SeekBar seekBar) { 118 | 119 | } 120 | 121 | @Override 122 | public void onStopTrackingTouch(SeekBar seekBar) { 123 | 124 | } 125 | }); 126 | 127 | root.findViewById(R.id.show_camera_lens_rect_bitmap).setOnClickListener(new View.OnClickListener() { 128 | @Override 129 | public void onClick(View v) { 130 | showCameraLensRectBitmap(); 131 | } 132 | }); 133 | } 134 | 135 | private void showCameraLensRectBitmap() { 136 | ivBackground.setDrawingCacheEnabled(true); 137 | Bitmap bitmap = ivBackground.getDrawingCache(true); 138 | bitmap = cameraLensView.cropCameraLensRectBitmap(bitmap, false); 139 | ImageView imageView = new ImageView(getContext()); 140 | imageView.setImageBitmap(bitmap); 141 | BottomShowDialog dialog = new BottomShowDialog(getContext()); 142 | dialog.setTitle("ShowBitmapInCameraLensRect"); 143 | dialog.setBitmap(bitmap); 144 | dialog.show(); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/fragments/CameraScannerMaskViewFragment.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import jsc.exam.com.cameramask.R; 12 | import jsc.kit.cameramask.CameraScannerMaskView; 13 | 14 | /** 15 | *
Email:1006368252@qq.com 16 | *
QQ:1006368252 17 | *
https://github.com/JustinRoom/CameraMaskDemo 18 | * 19 | * @author jiangshicheng 20 | */ 21 | public class CameraScannerMaskViewFragment extends Fragment { 22 | 23 | private CameraScannerMaskView cameraLensView; 24 | 25 | @Nullable 26 | @Override 27 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 28 | View root = inflater.inflate(R.layout.fragment_camera_scanner_mask_view, container, false); 29 | initView(root); 30 | return root; 31 | } 32 | 33 | private void initView(View root) { 34 | cameraLensView = root.findViewById(R.id.camera_scanner_mask_view); 35 | root.findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() { 36 | @Override 37 | public void onClick(View v) { 38 | cameraLensView.start(); 39 | getView().findViewById(R.id.btn_start).setEnabled(false); 40 | getView().findViewById(R.id.btn_pause).setEnabled(true); 41 | getView().findViewById(R.id.btn_resume).setEnabled(false); 42 | getView().findViewById(R.id.btn_stop).setEnabled(true); 43 | } 44 | }); 45 | root.findViewById(R.id.btn_pause).setOnClickListener(new View.OnClickListener() { 46 | @Override 47 | public void onClick(View v) { 48 | cameraLensView.pause(); 49 | getView().findViewById(R.id.btn_start).setEnabled(false); 50 | getView().findViewById(R.id.btn_pause).setEnabled(false); 51 | getView().findViewById(R.id.btn_resume).setEnabled(true); 52 | getView().findViewById(R.id.btn_stop).setEnabled(true); 53 | } 54 | }); 55 | root.findViewById(R.id.btn_resume).setOnClickListener(new View.OnClickListener() { 56 | @Override 57 | public void onClick(View v) { 58 | cameraLensView.resume(); 59 | getView().findViewById(R.id.btn_start).setEnabled(false); 60 | getView().findViewById(R.id.btn_pause).setEnabled(true); 61 | getView().findViewById(R.id.btn_resume).setEnabled(false); 62 | getView().findViewById(R.id.btn_stop).setEnabled(true); 63 | } 64 | }); 65 | root.findViewById(R.id.btn_stop).setOnClickListener(new View.OnClickListener() { 66 | @Override 67 | public void onClick(View v) { 68 | cameraLensView.stop(); 69 | getView().findViewById(R.id.btn_start).setEnabled(true); 70 | getView().findViewById(R.id.btn_pause).setEnabled(false); 71 | getView().findViewById(R.id.btn_resume).setEnabled(false); 72 | getView().findViewById(R.id.btn_stop).setEnabled(false); 73 | } 74 | }); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/fragments/ScannerBarViewFragment.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import jsc.exam.com.cameramask.R; 13 | import jsc.kit.cameramask.ScannerBarView; 14 | 15 | /** 16 | *
Email:1006368252@qq.com 17 | *
QQ:1006368252 18 | *
https://github.com/JustinRoom/CameraMaskDemo 19 | * 20 | * @author jiangshicheng 21 | */ 22 | public class ScannerBarViewFragment extends Fragment { 23 | 24 | private static final String TAG = "ScannerBarViewFragment"; 25 | ScannerBarView scannerBarView; 26 | 27 | @Override 28 | public void onCreate(@Nullable Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | Log.i(TAG, "onCreate: "); 31 | } 32 | 33 | @Nullable 34 | @Override 35 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 36 | Log.i(TAG, "onCreateView: "); 37 | View root = inflater.inflate(R.layout.fragment_scanner_bar_view, container, false); 38 | initView(root); 39 | return root; 40 | } 41 | 42 | private void initView(View root) { 43 | scannerBarView = root.findViewById(R.id.scanner_view); 44 | root.findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() { 45 | @Override 46 | public void onClick(View v) { 47 | scannerBarView.start(); 48 | } 49 | }); 50 | root.findViewById(R.id.btn_pause).setOnClickListener(new View.OnClickListener() { 51 | @Override 52 | public void onClick(View v) { 53 | scannerBarView.pause(); 54 | } 55 | }); 56 | root.findViewById(R.id.btn_resume).setOnClickListener(new View.OnClickListener() { 57 | @Override 58 | public void onClick(View v) { 59 | scannerBarView.resume(); 60 | } 61 | }); 62 | root.findViewById(R.id.btn_stop).setOnClickListener(new View.OnClickListener() { 63 | @Override 64 | public void onClick(View v) { 65 | scannerBarView.stop(); 66 | } 67 | }); 68 | root.findViewById(R.id.btn_change_size).setOnClickListener(new View.OnClickListener() { 69 | @Override 70 | public void onClick(View v) { 71 | ViewGroup.LayoutParams params = scannerBarView.getLayoutParams(); 72 | params.width = getResources().getDimensionPixelSize(R.dimen.space_256); 73 | scannerBarView.setLayoutParams(params); 74 | } 75 | }); 76 | root.findViewById(R.id.btn_change_img).setOnClickListener(new View.OnClickListener() { 77 | @Override 78 | public void onClick(View v) { 79 | scannerBarView.setScannerBarImageResource(R.drawable.camera_mask_scanner_line); 80 | } 81 | }); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/retrofit/ApiService.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.retrofit; 2 | 3 | import io.reactivex.Observable; 4 | import jsc.exam.com.cameramask.BuildConfig; 5 | import retrofit2.http.GET; 6 | 7 | public interface ApiService { 8 | 9 | @GET(BuildConfig.VERSION_URL) 10 | Observable getVersionInfo(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/retrofit/CustomHttpClient.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.retrofit; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.util.Pair; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import io.reactivex.annotations.NonNull; 14 | import jsc.exam.com.cameramask.utils.ConnectivityHelper; 15 | import okhttp3.Cache; 16 | import okhttp3.CacheControl; 17 | import okhttp3.Interceptor; 18 | import okhttp3.OkHttpClient; 19 | import okhttp3.Request; 20 | import okhttp3.Response; 21 | import okhttp3.logging.HttpLoggingInterceptor; 22 | 23 | /** 24 | * 25 | *
Email:1006368252@qq.com 26 | *
QQ:1006368252 27 | *
https://github.com/JustinRoom/CameraMaskDemo 28 | * 29 | * create time: 6/6/2018 6:02 PM 30 | * @author jiangshicheng 31 | */ 32 | public class CustomHttpClient { 33 | private boolean showLog = false; 34 | private int connectTimeout = 10_000; 35 | private int readTimeout = 10_000; 36 | private int writeTimeout = 10_000; 37 | private List> headers = null; 38 | private List interceptors = null; 39 | private Context context; 40 | private Cache cache; 41 | 42 | public CustomHttpClient setShowLog(boolean showLog) { 43 | this.showLog = showLog; 44 | return this; 45 | } 46 | 47 | public CustomHttpClient setConnectTimeout(int connectTimeout) { 48 | this.connectTimeout = connectTimeout; 49 | return this; 50 | } 51 | 52 | public CustomHttpClient setReadTimeout(int readTimeout) { 53 | this.readTimeout = readTimeout; 54 | return this; 55 | } 56 | 57 | public CustomHttpClient setWriteTimeout(int writeTimeout) { 58 | this.writeTimeout = writeTimeout; 59 | return this; 60 | } 61 | 62 | public CustomHttpClient addHeader(@NonNull Pair header) { 63 | if (headers == null) { 64 | headers = new ArrayList<>(); 65 | } 66 | headers.add(header); 67 | return this; 68 | } 69 | 70 | public CustomHttpClient addInterceptor(Interceptor interceptor) { 71 | if (interceptors == null) { 72 | interceptors = new ArrayList<>(); 73 | } 74 | interceptors.add(interceptor); 75 | return this; 76 | } 77 | 78 | 79 | public CustomHttpClient setContext(Application applicationContext) { 80 | this.context = applicationContext; 81 | return this; 82 | } 83 | 84 | /** 85 | * Set network cache config. 86 | * You must call method {@link #setContext(Application)} before calling this method. 87 | * 88 | * @param cacheFileName cache file name. 89 | * @param maxCacheSize cache max size. 90 | * @return CustomHttpClient 91 | */ 92 | public CustomHttpClient setCache(String cacheFileName, long maxCacheSize) { 93 | if (context != null){ 94 | File cacheFile = new File(context.getCacheDir(), cacheFileName); 95 | cache = new Cache(cacheFile, maxCacheSize); 96 | } 97 | return this; 98 | } 99 | 100 | public OkHttpClient createOkHttpClient() { 101 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 102 | builder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) 103 | .readTimeout(readTimeout, TimeUnit.MILLISECONDS) 104 | .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS); 105 | //拦截服务器端的Log日志并打印,如果未debug状态就打印日志,否则就什么都不做 106 | builder.addInterceptor(new HttpLoggingInterceptor().setLevel(showLog ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE)); 107 | 108 | if (headers != null && !headers.isEmpty()) 109 | builder.addInterceptor(createHeaderInterceptor()); 110 | 111 | if (interceptors != null && !interceptors.isEmpty()) { 112 | for (Interceptor it : interceptors) { 113 | builder.addInterceptor(it); 114 | } 115 | } 116 | 117 | if (cache != null) { 118 | builder.cache(cache); 119 | builder.addNetworkInterceptor(createCacheInterceptor()); 120 | } 121 | return builder.build(); 122 | } 123 | 124 | private Interceptor createHeaderInterceptor() { 125 | return new Interceptor() { 126 | @Override 127 | public Response intercept(Chain chain) throws IOException { 128 | Request original = chain.request(); 129 | Request.Builder requestBuilder = original.newBuilder(); 130 | for (Pair header : headers) { 131 | requestBuilder.addHeader(header.first, header.second); 132 | } 133 | Request request = requestBuilder.build(); 134 | return chain.proceed(request); 135 | } 136 | }; 137 | } 138 | 139 | private Interceptor createCacheInterceptor() { 140 | return new Interceptor() { 141 | @Override 142 | public Response intercept(Chain chain) throws IOException { 143 | Request request = chain.request(); 144 | // Add FORCE_CACHE cache control for each request if network is not available. 145 | if (!ConnectivityHelper.isNetworkAvailable(context)) { 146 | request = request.newBuilder() 147 | .cacheControl(CacheControl.FORCE_CACHE) 148 | .build(); 149 | } 150 | 151 | Response originalResponse = chain.proceed(request); 152 | if (ConnectivityHelper.isNetworkAvailable(context)) { 153 | String cacheControl = request.cacheControl().toString(); 154 | // Add cache control header for response same as request's while network is available. 155 | return originalResponse.newBuilder() 156 | .header("Cache-Control", cacheControl) 157 | .build(); 158 | } else { 159 | // Add cache control header for response to FORCE_CACHE while network is not available. 160 | return originalResponse.newBuilder() 161 | .header("Cache-Control", CacheControl.FORCE_CACHE.toString()) 162 | .build(); 163 | } 164 | } 165 | }; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/retrofit/CustomRetrofit.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.retrofit; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import io.reactivex.annotations.NonNull; 7 | import okhttp3.OkHttpClient; 8 | import retrofit2.CallAdapter; 9 | import retrofit2.Converter; 10 | import retrofit2.Retrofit; 11 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 12 | import retrofit2.converter.gson.GsonConverterFactory; 13 | import retrofit2.converter.scalars.ScalarsConverterFactory; 14 | 15 | /** 16 | *
Email:1006368252@qq.com 17 | *
QQ:1006368252 18 | *
https://github.com/JustinRoom/CameraMaskDemo 19 | * 20 | * @author jiangshicheng 21 | */ 22 | public class CustomRetrofit { 23 | private String baseUrl; 24 | private OkHttpClient okHttpClient; 25 | private List converterFactories = new ArrayList<>(); 26 | private List callAdapterFactories = new ArrayList<>(); 27 | private boolean isDefaultFactoriesAdded = false; 28 | 29 | public CustomRetrofit setBaseUrl(@NonNull String baseUrl) { 30 | this.baseUrl = baseUrl; 31 | return this; 32 | } 33 | 34 | public CustomRetrofit setOkHttpClient(@NonNull OkHttpClient okHttpClient) { 35 | this.okHttpClient = okHttpClient; 36 | return this; 37 | } 38 | 39 | private CustomRetrofit addConverterFactory(@NonNull Converter.Factory factory){ 40 | checkIsDefaultFactoriesAdded(); 41 | converterFactories.add(factory); 42 | return this; 43 | } 44 | 45 | private CustomRetrofit addCallAdapterFactory(@NonNull CallAdapter.Factory factory){ 46 | checkIsDefaultFactoriesAdded(); 47 | callAdapterFactories.add(factory); 48 | return this; 49 | } 50 | 51 | public Retrofit createRetrofit() { 52 | if (baseUrl == null || baseUrl.trim().length() == 0) 53 | throw new IllegalArgumentException("Base url can't be null."); 54 | 55 | if (okHttpClient == null) 56 | throw new IllegalArgumentException("You have not initialized OkHttpClient."); 57 | 58 | Retrofit.Builder builder = new Retrofit.Builder(); 59 | builder.baseUrl(baseUrl).client(okHttpClient); 60 | checkIsDefaultFactoriesAdded(); 61 | for (Converter.Factory factory : converterFactories) { 62 | builder.addConverterFactory(factory); 63 | } 64 | for (CallAdapter.Factory factory : callAdapterFactories) { 65 | builder.addCallAdapterFactory(factory); 66 | } 67 | return builder.build(); 68 | } 69 | 70 | private void checkIsDefaultFactoriesAdded(){ 71 | if (isDefaultFactoriesAdded) 72 | return; 73 | 74 | //增加返回值为String的支持 75 | converterFactories.add(ScalarsConverterFactory.create()); 76 | //增加返回值为Gson的支持(以实体类返回) 77 | converterFactories.add(GsonConverterFactory.create()); 78 | //增加返回值为Oservable的支持 79 | callAdapterFactories.add(RxJava2CallAdapterFactory.create()); 80 | isDefaultFactoriesAdded = true; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/retrofit/LoadingDialogObserver.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.retrofit; 2 | 3 | import android.app.Dialog; 4 | import android.content.DialogInterface; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import android.os.Message; 8 | 9 | import io.reactivex.Observer; 10 | import io.reactivex.disposables.Disposable; 11 | 12 | /** 13 | * 14 | *
Email:1006368252@qq.com 15 | *
QQ:1006368252 16 | *
https://github.com/JustinRoom/CameraMaskDemo 17 | * 18 | * create time: 6/7/2018 1:00 PM 19 | * @author jiangshicheng 20 | */ 21 | public abstract class LoadingDialogObserver implements Observer, DialogInterface.OnCancelListener { 22 | 23 | private final int SHOW_DIALOG = 0x6990; 24 | private final int HIDE_DIALOG = 0x6991; 25 | private Dialog loadingDialog; 26 | private boolean ifShowDialog; 27 | private Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() { 28 | @Override 29 | public boolean handleMessage(Message message) { 30 | switch (message.what) { 31 | case SHOW_DIALOG: 32 | if (loadingDialog != null && !loadingDialog.isShowing()) 33 | loadingDialog.show(); 34 | break; 35 | case HIDE_DIALOG: 36 | if (loadingDialog != null && loadingDialog.isShowing()) 37 | loadingDialog.dismiss(); 38 | break; 39 | } 40 | return true; 41 | } 42 | }); 43 | private Disposable disposable; 44 | 45 | /** 46 | * Constructor. 47 | */ 48 | public LoadingDialogObserver() { 49 | this(null); 50 | } 51 | 52 | /** 53 | * Constructor. 54 | * @param loadingDialog loading dialog 55 | */ 56 | public LoadingDialogObserver(Dialog loadingDialog) { 57 | this(loadingDialog, true); 58 | } 59 | 60 | /** 61 | * Constructor. 62 | * 63 | * @param loadingDialog loading dialog 64 | * @param ifShowDialog Show loadingDialog if true else not. 65 | */ 66 | public LoadingDialogObserver(Dialog loadingDialog, boolean ifShowDialog) { 67 | this.loadingDialog = loadingDialog; 68 | this.ifShowDialog = ifShowDialog; 69 | if (loadingDialog != null) 70 | loadingDialog.setOnCancelListener(this); 71 | } 72 | 73 | @Override 74 | public void onSubscribe(Disposable d) { 75 | disposable = d; 76 | if (ifShowDialog) 77 | handler.sendEmptyMessage(SHOW_DIALOG); 78 | onStart(d); 79 | } 80 | 81 | @Override 82 | public void onNext(T t) { 83 | try { 84 | onResult(t); 85 | } catch (Exception e){ 86 | onError(e); 87 | } 88 | } 89 | 90 | @Override 91 | public void onError(Throwable e) { 92 | if (ifShowDialog) 93 | handler.sendEmptyMessage(HIDE_DIALOG); 94 | onException(e); 95 | onCompleteOrCancel(disposable); 96 | } 97 | 98 | @Override 99 | public void onComplete() { 100 | if (ifShowDialog) 101 | handler.sendEmptyMessage(HIDE_DIALOG); 102 | onCompleteOrCancel(disposable); 103 | } 104 | 105 | @Override 106 | public void onCancel(DialogInterface dialog) { 107 | disposable.dispose(); 108 | onCompleteOrCancel(disposable); 109 | } 110 | 111 | /** 112 | * Show loading dialog here if necessary. 113 | * @param disposable disposable, the same as the return of {@link io.reactivex.Observable#subscribe(Observer)}. 114 | */ 115 | public abstract void onStart(Disposable disposable); 116 | 117 | /** 118 | * Call back the response. 119 | * @param t response 120 | */ 121 | public abstract void onResult(T t); 122 | 123 | /** 124 | * Call back when a exception appears. 125 | * @param e exception 126 | */ 127 | public abstract void onException(Throwable e); 128 | 129 | /** 130 | * Call back when {@link Observer#onComplete()} or loading dialog is canceled. 131 | * @param disposable disposable, the same as the return of {@link io.reactivex.Observable#subscribe(Observer)}. 132 | */ 133 | public abstract void onCompleteOrCancel(Disposable disposable); 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/utils/CompatResourceUtils.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.utils; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.support.annotation.ColorRes; 6 | import android.support.annotation.DimenRes; 7 | import android.support.annotation.DrawableRes; 8 | import android.support.annotation.NonNull; 9 | import android.support.v4.app.Fragment; 10 | import android.util.TypedValue; 11 | import android.view.View; 12 | 13 | /** 14 | *
Email:1006368252@qq.com 15 | *
QQ:1006368252 16 | *
https://github.com/JustinRoom/CameraMaskDemo 17 | * 18 | * @author jiangshicheng 19 | */ 20 | public class CompatResourceUtils { 21 | 22 | //color 23 | public static int getColor(@NonNull Context context, @ColorRes int resId){ 24 | int color; 25 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){ 26 | color = context.getResources().getColor(resId, context.getTheme()); 27 | } else { 28 | color = context.getResources().getColor(resId); 29 | } 30 | return color; 31 | } 32 | 33 | //drawable-xxhdpi 34 | public static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId){ 35 | Drawable drawable; 36 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){ 37 | drawable = context.getResources().getDrawable(resId, context.getTheme()); 38 | } else { 39 | drawable = context.getResources().getDrawable(resId); 40 | } 41 | return drawable; 42 | } 43 | 44 | //dimension 45 | public static int getDimensionPixelSize(@NonNull Context context, @DimenRes int id){ 46 | return context.getResources().getDimensionPixelSize(id); 47 | } 48 | 49 | public static int getDimensionPixelSize(@NonNull View view, @DimenRes int id){ 50 | return view.getResources().getDimensionPixelSize(id); 51 | } 52 | 53 | public static int getDimensionPixelSize(@NonNull Fragment fragment, @DimenRes int id){ 54 | return fragment.getResources().getDimensionPixelSize(id); 55 | } 56 | 57 | public static int dp2Px(@NonNull Context context, float dp){ 58 | return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()) + .5f); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/utils/ConnectivityHelper.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.utils; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | import java.util.Objects; 8 | 9 | /** 10 | * 11 | * ConnectivityHelper 网络工具 12 | * 13 | * 14 | */ 15 | public class ConnectivityHelper { 16 | 17 | public static boolean isNetworkAvailable(Context context) { 18 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 19 | Objects.requireNonNull(cm); 20 | NetworkInfo networkInfo = cm.getActiveNetworkInfo(); 21 | return networkInfo != null && networkInfo.isAvailable(); 22 | } 23 | 24 | /** 25 | * 判断网络是否可用 26 | * 27 | * @param context context 28 | * @return boolean 29 | */ 30 | public static boolean isConnectivityAvailable(Context context) { 31 | // 判断网络是否可用 32 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 33 | Objects.requireNonNull(cm); 34 | NetworkInfo info = cm.getActiveNetworkInfo(); 35 | if (info == null || !info.isConnected()) { 36 | return false; 37 | } 38 | return info.isAvailable() || info.isRoaming(); 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/utils/WindowUtils.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.utils; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.drawable.Drawable; 7 | import android.os.Build; 8 | import android.support.annotation.NonNull; 9 | import android.util.TypedValue; 10 | 11 | /** 12 | *
Email:1006368252@qq.com 13 | *
QQ:1006368252 14 | *
https://github.com/JustinRoom/CameraMaskDemo 15 | * 16 | * @author jiangshicheng 17 | */ 18 | public final class WindowUtils { 19 | 20 | /** 21 | * Get status bar height. 22 | * 23 | * @param context context 24 | * @return the height of status bar 25 | */ 26 | public static int getStatusBarHeight(@NonNull Context context) { 27 | int statusBarHeight = 0; 28 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); 29 | if (resourceId > 0) { 30 | statusBarHeight = CompatResourceUtils.getDimensionPixelSize(context, resourceId); 31 | } 32 | return statusBarHeight; 33 | } 34 | 35 | /** 36 | * Get action bar height. 37 | * 38 | * @param context context 39 | * @return the height of action bar 40 | */ 41 | public static int getActionBarSize(@NonNull Context context) { 42 | TypedValue typedValue = new TypedValue(); 43 | int[] attribute = new int[]{android.R.attr.actionBarSize}; 44 | TypedArray array = context.obtainStyledAttributes(typedValue.resourceId, attribute); 45 | int actionBarSize = array.getDimensionPixelSize(0, 0); 46 | array.recycle(); 47 | return actionBarSize; 48 | } 49 | 50 | /** 51 | * Get system selectable item background borderless. 52 | * @param context context 53 | * @return selectable item background borderless 54 | */ 55 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 56 | public static Drawable getSelectableItemBackgroundBorderless(Context context){ 57 | TypedValue typedValue = new TypedValue(); 58 | context.getTheme().resolveAttribute(android.R.attr.selectableItemBackgroundBorderless, typedValue, true); 59 | int[] attribute = new int[]{android.R.attr.selectableItemBackgroundBorderless}; 60 | TypedArray typedArray = context.obtainStyledAttributes(typedValue.resourceId, attribute); 61 | Drawable drawable = typedArray.getDrawable(0); 62 | typedArray.recycle(); 63 | return drawable; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/widgets/DotView.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.widgets; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.graphics.Outline; 6 | import android.graphics.Path; 7 | import android.os.Build; 8 | import android.support.annotation.IntDef; 9 | import android.support.annotation.IntRange; 10 | import android.support.annotation.Nullable; 11 | import android.support.v7.widget.AppCompatTextView; 12 | import android.util.AttributeSet; 13 | import android.util.Log; 14 | import android.view.Gravity; 15 | import android.view.View; 16 | import android.view.ViewOutlineProvider; 17 | 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.RetentionPolicy; 20 | 21 | /** 22 | *
Email:1006368252@qq.com 23 | *
QQ:1006368252 24 | *
https://github.com/JustinRoom/CameraMaskDemo 25 | * 26 | * @author jiangshicheng 27 | */ 28 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 29 | public class DotView extends AppCompatTextView { 30 | 31 | public final static int CIRCULAR = 0; 32 | public final static int SQUARE = 1; 33 | public final static int ROUND_CORNER_SQUARE = 2; 34 | public final static int TRIANGLE = 3; 35 | @IntDef({CIRCULAR, SQUARE, ROUND_CORNER_SQUARE, TRIANGLE}) 36 | @Retention(RetentionPolicy.SOURCE) 37 | public @interface DotShape { 38 | } 39 | 40 | int shape = CIRCULAR; 41 | float radius = 0; 42 | int unReadCount = 0; 43 | 44 | public DotView(Context context) { 45 | super(context); 46 | initAttr(context, null, 0); 47 | } 48 | 49 | public DotView(Context context, @Nullable AttributeSet attrs) { 50 | super(context, attrs); 51 | initAttr(context, attrs, 0); 52 | } 53 | 54 | public DotView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 55 | super(context, attrs, defStyleAttr); 56 | initAttr(context, attrs, defStyleAttr); 57 | } 58 | 59 | private void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 60 | setGravity(Gravity.CENTER); 61 | setClipToOutline(true); 62 | setOutlineProvider(new ViewOutlineProvider() { 63 | @Override 64 | public void getOutline(View view, Outline outline) { 65 | switch (shape){ 66 | case CIRCULAR: 67 | outline.setOval(0, 0, view.getWidth(), view.getHeight()); 68 | break; 69 | case SQUARE: 70 | outline.setRect(0, 0, view.getWidth(), view.getHeight()); 71 | break; 72 | case ROUND_CORNER_SQUARE: 73 | outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), radius); 74 | break; 75 | case TRIANGLE: 76 | Path path = new Path(); 77 | path.moveTo(view.getWidth() / 2.0f, 0); 78 | path.lineTo(0, view.getHeight()); 79 | path.lineTo(view.getWidth(), view.getHeight()); 80 | path.close(); 81 | Log.i("DotView", "getOutline: isConvex =" + path.isConvex()); 82 | outline.setConvexPath(path); 83 | break; 84 | } 85 | } 86 | }); 87 | } 88 | 89 | public int getUnReadCount() { 90 | return unReadCount; 91 | } 92 | 93 | public void setUnReadCount(@IntRange(from = 0) int unReadCount) { 94 | this.unReadCount = unReadCount; 95 | if (unReadCount > 99) 96 | setText("99+"); 97 | else if (unReadCount > 0) 98 | setText(String.valueOf(unReadCount)); 99 | else 100 | setText(""); 101 | } 102 | 103 | public void setShape(@DotShape int shape) { 104 | setShape(shape, 0); 105 | } 106 | 107 | public void setShape(@DotShape int dotShape, float roundRadius) { 108 | this.shape = dotShape; 109 | this.radius = roundRadius; 110 | invalidateOutline(); 111 | } 112 | 113 | @Override 114 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 115 | super.onMeasure(widthMeasureSpec, widthMeasureSpec); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/widgets/JSCItemLayout.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.widgets; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Color; 6 | import android.support.annotation.DrawableRes; 7 | import android.support.annotation.Nullable; 8 | import android.support.annotation.StringRes; 9 | import android.support.v7.widget.AppCompatImageView; 10 | import android.util.AttributeSet; 11 | import android.util.TypedValue; 12 | import android.view.Gravity; 13 | import android.view.ViewGroup; 14 | import android.widget.FrameLayout; 15 | import android.widget.ImageView; 16 | import android.widget.LinearLayout; 17 | import android.widget.TextView; 18 | 19 | import jsc.exam.com.cameramask.R; 20 | import jsc.exam.com.cameramask.utils.CompatResourceUtils; 21 | 22 | /** 23 | *
Email:1006368252@qq.com 24 | *
QQ:1006368252 25 | *
https://github.com/JustinRoom/CameraMaskDemo 26 | * 27 | * @author jiangshicheng 28 | */ 29 | public class JSCItemLayout extends FrameLayout { 30 | 31 | private ImageView iconView; 32 | private TextView labelView; 33 | private TextView valueView; 34 | private DotView dotView; 35 | private ImageView arrowView; 36 | 37 | public JSCItemLayout(Context context) { 38 | super(context); 39 | initAttr(context, null, 0); 40 | } 41 | 42 | public JSCItemLayout(Context context, @Nullable AttributeSet attrs) { 43 | super(context, attrs); 44 | initAttr(context, attrs, 0); 45 | } 46 | 47 | public JSCItemLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 48 | super(context, attrs, defStyleAttr); 49 | initAttr(context, attrs, defStyleAttr); 50 | } 51 | 52 | private void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 53 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.JSCItemLayout, defStyleAttr, 0); 54 | 55 | LayoutParams contentParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 56 | contentParams.gravity = Gravity.CENTER_VERTICAL; 57 | LinearLayout layout = new LinearLayout(context); 58 | layout.setOrientation(LinearLayout.HORIZONTAL); 59 | layout.setGravity(Gravity.CENTER_VERTICAL); 60 | addView(layout, contentParams); 61 | 62 | //icon 63 | iconView = new AppCompatImageView(context); 64 | iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 65 | layout.addView(iconView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 66 | iconView.setImageResource(a.getResourceId(R.styleable.JSCItemLayout_il_icon, R.drawable.kit_ic_assignment_blue_24dp)); 67 | 68 | //label 69 | labelView = new TextView(context); 70 | labelView.setTextColor(0xFF666666); 71 | labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); 72 | layout.addView(labelView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 73 | if (a.hasValue(R.styleable.JSCItemLayout_il_label)) { 74 | labelView.setText(a.getString(R.styleable.JSCItemLayout_il_label)); 75 | } 76 | 77 | //value 78 | valueView = new TextView(context); 79 | valueView.setTextColor(0xFF333333); 80 | valueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); 81 | layout.addView(valueView, new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1)); 82 | if (a.hasValue(R.styleable.JSCItemLayout_il_value)) { 83 | valueView.setText(a.getString(R.styleable.JSCItemLayout_il_value)); 84 | } 85 | 86 | //red dot 87 | int size = CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_8); 88 | dotView = new DotView(context); 89 | dotView.setBackgroundColor(Color.RED); 90 | dotView.setTextColor(Color.WHITE); 91 | if (a.hasValue(R.styleable.JSCItemLayout_il_dotSize)){ 92 | size = a.getDimensionPixelSize(R.styleable.JSCItemLayout_il_dotSize, 0); 93 | } 94 | dotView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); 95 | layout.addView(dotView, new LinearLayout.LayoutParams(size, size)); 96 | showDotView(a.getBoolean(R.styleable.JSCItemLayout_il_showDot, false)); 97 | 98 | //right arrow 99 | arrowView = new AppCompatImageView(context); 100 | arrowView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 101 | layout.addView(arrowView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 102 | arrowView.setImageResource(a.getResourceId(R.styleable.JSCItemLayout_il_arrowIcon, R.drawable.kit_ic_chevron_right_gray_24dp)); 103 | 104 | a.recycle(); 105 | } 106 | 107 | public ImageView getIconView() { 108 | return iconView; 109 | } 110 | 111 | public TextView getLabelView() { 112 | return labelView; 113 | } 114 | 115 | public TextView getValueView() { 116 | return valueView; 117 | } 118 | 119 | public DotView getDotView() { 120 | return dotView; 121 | } 122 | 123 | public ImageView getArrowView() { 124 | return arrowView; 125 | } 126 | 127 | public void showIconView(boolean show) { 128 | iconView.setVisibility(show ? VISIBLE : GONE); 129 | } 130 | 131 | public void showDotView(boolean show) { 132 | dotView.setVisibility(show ? VISIBLE : GONE); 133 | } 134 | 135 | public void showArrowView(boolean show){ 136 | arrowView.setVisibility(show ? VISIBLE : GONE); 137 | } 138 | 139 | public int getUnreadCount() { 140 | return dotView.getUnReadCount(); 141 | } 142 | 143 | public void setUnreadCount(int unreadCount) { 144 | dotView.setUnReadCount(unreadCount); 145 | } 146 | 147 | public void setIcon(@DrawableRes int resId) { 148 | iconView.setImageResource(resId); 149 | } 150 | 151 | public void setLabel(CharSequence label) { 152 | labelView.setText(label); 153 | } 154 | 155 | public void setLabel(@StringRes int resId) { 156 | labelView.setText(resId); 157 | } 158 | 159 | public void setValue(CharSequence label) { 160 | valueView.setText(label); 161 | } 162 | 163 | public void setValue(@StringRes int resId) { 164 | valueView.setText(resId); 165 | } 166 | 167 | public void setArrow(@DrawableRes int resId) { 168 | arrowView.setImageResource(resId); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/widgets/SquareImageView.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.widgets; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.AppCompatImageView; 5 | import android.util.AttributeSet; 6 | 7 | public class SquareImageView extends AppCompatImageView { 8 | public SquareImageView(Context context) { 9 | super(context); 10 | } 11 | 12 | public SquareImageView(Context context, AttributeSet attrs) { 13 | super(context, attrs); 14 | } 15 | 16 | public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) { 17 | super(context, attrs, defStyleAttr); 18 | } 19 | 20 | @Override 21 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 22 | super.onMeasure(widthMeasureSpec, widthMeasureSpec); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/jsc/exam/com/cameramask/widgets/dialog/BottomShowDialog.java: -------------------------------------------------------------------------------- 1 | package jsc.exam.com.cameramask.widgets.dialog; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Color; 7 | import android.os.Bundle; 8 | import android.support.annotation.NonNull; 9 | import android.util.TypedValue; 10 | import android.view.Gravity; 11 | import android.view.ViewGroup; 12 | import android.view.Window; 13 | import android.view.WindowManager; 14 | import android.widget.ImageView; 15 | import android.widget.LinearLayout; 16 | import android.widget.TextView; 17 | 18 | import jsc.exam.com.cameramask.R; 19 | 20 | /** 21 | *
Email:1006368252@qq.com 22 | *
QQ:1006368252 23 | *
https://github.com/JustinRoom/CameraMaskDemo 24 | * 25 | * @author jiangshicheng 26 | */ 27 | public class BottomShowDialog extends Dialog { 28 | 29 | CharSequence title; 30 | Bitmap bitmap; 31 | 32 | public BottomShowDialog( @NonNull Context context) { 33 | this(context, R.style.Theme_AppCompat_Dialog); 34 | } 35 | 36 | public BottomShowDialog( @NonNull Context context, int themeResId) { 37 | super(context, themeResId); 38 | } 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | requestWindowFeature(Window.FEATURE_NO_TITLE); 44 | LinearLayout layout = new LinearLayout(getContext()); 45 | layout.setOrientation(LinearLayout.VERTICAL); 46 | layout.setGravity(Gravity.CENTER_HORIZONTAL); 47 | // 48 | TextView textView = new TextView(getContext()); 49 | textView.setBackgroundColor(getContext().getResources().getColor(R.color.colorAccent)); 50 | textView.setGravity(Gravity.CENTER_HORIZONTAL); 51 | textView.setText(title); 52 | textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); 53 | textView.setTextColor(0xFF333333); 54 | textView.setPadding(0, 16, 0, 16); 55 | layout.addView(textView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 56 | // 57 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 58 | params.topMargin = 24; 59 | params.bottomMargin = 24; 60 | ImageView imageView = new ImageView(getContext()); 61 | imageView.setImageBitmap(bitmap); 62 | layout.addView(imageView, params); 63 | setContentView(layout); 64 | 65 | if (getWindow() != null) { 66 | getWindow().setGravity(Gravity.BOTTOM); 67 | getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT); 68 | getWindow().setBackgroundDrawable(null); 69 | getWindow().getDecorView().setBackgroundColor(Color.WHITE); 70 | } 71 | } 72 | 73 | @Override 74 | public void show() { 75 | super.show(); 76 | } 77 | 78 | @Override 79 | public void setTitle(CharSequence title) { 80 | this.title = title; 81 | } 82 | 83 | public void setBitmap(Bitmap bitmap) { 84 | this.bitmap = bitmap; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ripple_round_corner_white_r4.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /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-xxhdpi/btn_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinRoom/CameraMaskDemo/f9fa5ca8346c4f8412a494239e0a7c39dfd5742f/app/src/main/res/drawable-xxhdpi/btn_background.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/default_camera_lens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinRoom/CameraMaskDemo/f9fa5ca8346c4f8412a494239e0a7c39dfd5742f/app/src/main/res/drawable-xxhdpi/default_camera_lens.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_chevron_left_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinRoom/CameraMaskDemo/f9fa5ca8346c4f8412a494239e0a7c39dfd5742f/app/src/main/res/drawable-xxhdpi/ic_chevron_left_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/kit_ic_assignment_blue_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinRoom/CameraMaskDemo/f9fa5ca8346c4f8412a494239e0a7c39dfd5742f/app/src/main/res/drawable-xxhdpi/kit_ic_assignment_blue_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/kit_ic_chevron_right_gray_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinRoom/CameraMaskDemo/f9fa5ca8346c4f8412a494239e0a7c39dfd5742f/app/src/main/res/drawable-xxhdpi/kit_ic_chevron_right_gray_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/beauty_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinRoom/CameraMaskDemo/f9fa5ca8346c4f8412a494239e0a7c39dfd5742f/app/src/main/res/drawable/beauty_bg.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/camera_mask_demo_qr_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinRoom/CameraMaskDemo/f9fa5ca8346c4f8412a494239e0a7c39dfd5742f/app/src/main/res/drawable/camera_mask_demo_qr_code.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ripple_round_corner_white_r4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_abount.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 22 | 23 | 31 | 32 | 40 | 41 | 49 | 50 | 51 | 52 | 57 | 58 |