├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── ykbjson │ │ │ └── app │ │ │ └── simplepermission │ │ │ ├── MainActivity.java │ │ │ ├── SecondActivity.java │ │ │ ├── TestApplication.java │ │ │ └── TestFragment.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_dashboard_black_24dp.xml │ │ ├── ic_home_black_24dp.xml │ │ ├── ic_launcher_background.xml │ │ └── ic_notifications_black_24dp.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_second.xml │ │ └── fragment_test.xml │ │ ├── menu │ │ └── navigation.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 │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── ykbjson │ └── app │ └── simplepermission │ └── ExampleUnitTest.java ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── permissionplugin ├── .gitignore ├── build.gradle └── src │ └── main │ ├── groovy │ └── com │ │ └── ykbjson │ │ └── lib │ │ └── plugin │ │ ├── SimplePermissionPlugin.groovy │ │ ├── SimplePermissionPluginInject.groovy │ │ └── SimplePermissionTransform.groovy │ └── resources │ └── META-INF │ └── gradle-plugins │ └── com.ykbjson.simplepermission.properties ├── settings.gradle ├── simplepermission ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── ykbjson │ │ └── lib │ │ └── simplepermission │ │ ├── Permissions.java │ │ ├── PermissionsManager.java │ │ ├── PermissionsRequestCallback.java │ │ └── PermissionsResultAction.java │ └── res │ └── values │ └── strings.xml └── simplepermission_ano ├── .gitignore ├── build.gradle └── src └── main └── java └── com └── ykbjson └── lib └── simplepermission └── ano ├── PermissionNotify.java └── PermissionRequest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .idea 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 一、SimplePermission简介 2 | 基于注解调用的Android权限申请库,无需你手写申请权限的代码,也无需你在Activity或Fragment的onRequestPermissionsResult方法里写任何代码,只需在需要申请权限的类和方法上加上对应的注解即可,简单易用。 3 | 4 | 关于本库的介绍和思路请戳 5 | 6 | [Android权限管理的探索-csdn](https://blog.csdn.net/yankebin/article/details/83863684) 7 | 8 | [Android权限管理的探索-github博客](https://ykbjson.github.io/2018/11/08/Android%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86%E7%9A%84%E6%8E%A2%E7%B4%A2/) 9 | 10 | # 二、如何引用 11 | 因为这个功能是基于gradle插件来实现的,所以和一般权限库有所不同,需要在你的工程根目录的build.gradle文件相似位置里加上如下代码(看有注释的地方)。当前最新版本为 12 | [ ![Download](https://api.bintray.com/packages/ykbjson/maven/simplepermissionplugin/images/download.svg) ](https://bintray.com/ykbjson/maven/simplepermissionplugin/_latestVersion) 13 | 14 | 15 | 16 | buildscript { 17 | repositories { 18 | google() 19 | jcenter() 20 | mavenCentral() 21 | mavenLocal() 22 | //这是我bintray的maven地址 23 | maven { url 'https://dl.bintray.com/ykbjson/maven' } 24 | } 25 | dependencies { 26 | classpath 'com.android.tools.build:gradle:3.1.1' 27 | //这是simplepermissionplugin的classpath 28 | classpath 'com.ykbjson.simplepermission:simplepermissionplugin:1.0.1' 29 | } 30 | } 31 | 32 | 33 | allprojects { 34 | repositories { 35 | google() 36 | jcenter() 37 | mavenCentral() 38 | mavenLocal() 39 | //这是我bintray的maven地址 40 | maven { url 'https://dl.bintray.com/ykbjson/maven' } 41 | } 42 | } 43 | 44 | 45 | 46 | 然后在app module的build.gradle文件里引入apply simplepermissionplugin插件 47 | 48 | 49 | 50 | apply plugin: 'com.ykbjson.simplepermission' 51 | 52 | 53 | 54 | 最后在app module的build.gradle文件里引入simpleprmission库和simplepermission_ano库,即可大功告成 55 | 56 | 57 | 58 | implementation 'com.ykbjson.simplepermission:simplepermission:1.0.1' 59 | implementation 'com.ykbjson.simplepermission:simplepermission_ano:1.0.0' 60 | 61 | 62 | 63 | # 三、如何使用 64 | 65 | 使用非常简单。 66 | 首先,在需要申请权限的Activity或Fragment类上加上注解@PermissionNotify 67 | 68 | 69 | 70 | @PermissionNotify 71 | public class MainActivity extends AppCompatActivity implements View.OnClickListener 72 | private TextView mTextMessage; 73 | protected void onCreate(Bundle savedInstanceState) { 74 | super.onCreate(savedInstanceState); 75 | setContentView(R.layout.activity_main); 76 | mTextMessage = (TextView) findViewById(R.id.message); 77 | 78 | mTextMessage.setOnClickListener(this); 79 | } 80 | 81 | private void setText(final String text) { 82 | mTextMessage.setText(text); 83 | } 84 | 85 | @Override 86 | public void onClick(View v) { 87 | setText("哈哈哈哈哈哈"); 88 | } 89 | } 90 | 91 | 92 | 93 | 然后在Activity或Fragment的某个方法上加上注解@PermissionRequest 94 | 95 | 96 | 97 | 98 | @PermissionRequest( 99 | requestCode = 10010, 100 | requestPermissions = {Manifest.permission.ACCESS_FINE_LOCATION, 101 | Manifest.permission.ACCESS_COARSE_LOCATION, 102 | Manifest.permission.READ_CONTACTS} 103 | ,needReCall = true 104 | ) 105 | private void setText(String text) { 106 | mTextMessage.setText(text); 107 | } 108 | 109 | 110 | 111 | 然后。。。。。。结束啦!!!!编译结束后其实MainActivity里的代码大致会变成如下的样子 112 | 113 | 114 | 115 | @PermissionNotify 116 | public class MainActivity extends AppCompatActivity implements OnClickListener, PermissionsRequestCallback { 117 | private TextView mTextMessage; 118 | private final Map requestPermissionMethodParams = new HashMap(); 119 | 120 | public MainActivity() { 121 | } 122 | 123 | protected void onCreate(Bundle savedInstanceState) { 124 | super.onCreate(savedInstanceState); 125 | this.setContentView(2131361818); 126 | 127 | this.mTextMessage.setOnClickListener(this); 128 | } 129 | 130 | @PermissionRequest( 131 | requestCode = 10010, 132 | requestPermissions = {"android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.READ_CONTACTS"}, 133 | needReCall = true 134 | ) 135 | private void setText(String text) { 136 | String[] var2 = new String[]{"android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.READ_CONTACTS"}; 137 | boolean var3 = PermissionsManager.getInstance().hasAllPermissions(this, var2); 138 | if (!var3) { 139 | ArrayList var4 = new ArrayList(); 140 | var4.add(text); 141 | this.requestPermissionMethodParams.put(10010, var4); 142 | PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(10010, this, var2, this); 143 | } else { 144 | this.mTextMessage.setText(text); 145 | } 146 | } 147 | 148 | public void onClick(View v) { 149 | this.setText("哈哈哈哈哈哈"); 150 | } 151 | 152 | public void onGranted(int var1, String var2) { 153 | } 154 | 155 | public void onDenied(int var1, String var2) { 156 | } 157 | 158 | public void onDeniedForever(int var1, String var2) { 159 | } 160 | 161 | public void onFailure(int var1, String[] var2) { 162 | } 163 | 164 | public void onSuccess(int var1) { 165 | Object var2 = this.requestPermissionMethodParams.get(var1); 166 | if (var1 == 10010) { 167 | this.setText((String)((List)var2).get(0)); 168 | } 169 | } 170 | 171 | public void onRequestPermissionsResult(int var1, String[] var2, int[] var3) { 172 | PermissionsManager.getInstance().notifyPermissionsChange(var2, var3); 173 | super.onRequestPermissionsResult(var1, var2, var3); 174 | } 175 | } 176 | 177 | 178 | 179 | 180 | 181 | 182 | 当然,如果你觉得使用注解会有诸多限制(请看下面第四条提到的“一些限制”),你也可以直接使用simplepermission库来实现权限的申请,类似代码如下 183 | 184 | 185 | 186 | 187 | private void setText(final String text) { 188 | PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(0, this, 189 | new String[]{Manifest.permission.READ_CONTACTS}, new PermissionsRequestCallback() { 190 | @Override 191 | public void onGranted(int requestCode, String permission) { 192 | 193 | } 194 | 195 | @Override 196 | public void onDenied(int requestCode, String permission) { 197 | 198 | } 199 | 200 | @Override 201 | public void onDeniedForever(int requestCode, String permission) { 202 | 203 | } 204 | 205 | @Override 206 | public void onFailure(int requestCode, String[] deniedPermissions) { 207 | 208 | } 209 | 210 | @Override 211 | public void onSuccess(int requestCode) { 212 | mTextMessage.setText(text); 213 | } 214 | }); 215 | } 216 | 217 | 218 | 219 | Android M及以上版本,需要在Activit或Fragment重载onRequestPermissionsResult方法,并且在该方法内部加入PermissionsManager.getInstance().notifyPermissionsChange(permissions,grantResults),类似如下代码 220 | 221 | 222 | 223 | @Override 224 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 225 | PermissionsManager.getInstance().notifyPermissionsChange(permissions,grantResults); 226 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 227 | } 228 | 229 | 230 | 231 | 232 | # 四、一些限制 233 | 234 | ##### 4.1 由于本人能力有限,本库目前还不支持在“带返回值”的方法上去加@PermissionRequest注解并且@PermissionRequest的needRecall设置为true,当然,needRecall设置为false时,是支持的。因为本库的原理是这样的:添加了@PermissionRequest注解的方法,我会先判断当前程序是否已经有了@PermissionRequest的requestPermissions里包含的权限,如果没有,则插入申请权限的代码,如果@PermissionRequest的needRecall为true,则需要存储方法的参数,以备权限回调成功的时候在调用此方法,然后插入“return”,结束方法执行;如果@PermissionRequest的needRecall为false,则这里只插入申请权限的代码,不在干预此方法的后续逻辑。 235 | 236 | ##### 4.2 本库目前还不支持在内部类里面的方法上加@PermissionRequest注解,因为permission权限申请库必要的一个参数是Activit或Fragment,如果是在内部类里面使用,我目前还无法得知如何获取该内部类持有的Activit或Fragment,尤其是在多层内部类嵌套的时候。 237 | 238 | ##### 4.3 本库目前还不支持在static方法上添加@PermissionRequest注解,因为用javassit插入“MainActivity.this”类似语句会报错,我暂时还没找到解决办法 239 | 240 | ##### 4.4 本库因为修改了class文件插入了一些代码,很有可能会使应用程序出现multiDex异常,所以,在需要的时候,最好让你的程序支持multiDex 241 | 242 | ##### 4.5 多个添加了@PermissionRequest注解的方法的requestCode千万不要相同,不然在权限申请成功回调的地方获取申请权限方法的参数会出现问题,导致你的程序会出现一些无法预知的错误 243 | 244 | ## License 245 | 246 | 247 | 248 | Copyright 2018 ykbjson 249 | 250 | Licensed under the Apache License, Version 2.0 (the "License"); 251 | you may not use this file except in compliance with the License. 252 | You may obtain a copy of the License at 253 | 254 | http://www.apache.org/licenses/LICENSE-2.0 255 | 256 | Unless required by applicable law or agreed to in writing, software 257 | distributed under the License is distributed on an "AS IS" BASIS, 258 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 259 | See the License for the specific language governing permissions and 260 | limitations under the License. 261 | 262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | //com.hc.gradle为resources/META-INF/gradle-plugins 3 | //permissionplugin下的properties文件名称 4 | //apply plugin: 'com.ykbjson.simplepermission' 5 | 6 | android { 7 | compileSdkVersion 27 8 | defaultConfig { 9 | applicationId "com.ykbjson.app.simplepermission" 10 | minSdkVersion 17 11 | targetSdkVersion 27 12 | versionCode 1 13 | versionName "1.0" 14 | multiDexEnabled = true 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | // implementation 'com.ykbjson.simplepermission:simplepermission:1.0.1' 28 | // implementation 'com.ykbjson.simplepermission:simplepermission_ano:1.0.0' 29 | 30 | implementation project(':simplepermission') 31 | implementation project(':simplepermission_ano') 32 | 33 | implementation 'com.android.support:appcompat-v7:27.0.2' 34 | implementation 'com.android.support:design:27.0.2' 35 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 36 | implementation 'com.android.support:support-vector-drawable:27.0.2' 37 | implementation 'com.android.support:multidex:1.0.1' 38 | testImplementation 'junit:junit:4.12' 39 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 40 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 41 | } 42 | 43 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/ykbjson/app/simplepermission/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.app.simplepermission; 2 | 3 | import android.Manifest; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.design.widget.BottomNavigationView; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.TextView; 12 | 13 | import com.ykbjson.lib.simplepermission.ano.PermissionNotify; 14 | import com.ykbjson.lib.simplepermission.ano.PermissionRequest; 15 | 16 | @PermissionNotify 17 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 18 | 19 | private TextView mTextMessage; 20 | 21 | private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener 22 | = new BottomNavigationView.OnNavigationItemSelectedListener() { 23 | 24 | @Override 25 | public boolean onNavigationItemSelected(@NonNull MenuItem item) { 26 | switch (item.getItemId()) { 27 | case R.id.navigation_home: 28 | mTextMessage.setText(R.string.title_home); 29 | return true; 30 | case R.id.navigation_dashboard: 31 | mTextMessage.setText(R.string.title_dashboard); 32 | return true; 33 | case R.id.navigation_notifications: 34 | mTextMessage.setText(R.string.title_notifications); 35 | return true; 36 | } 37 | return false; 38 | } 39 | }; 40 | 41 | @Override 42 | protected void onCreate(final Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | mTextMessage = (TextView) findViewById(R.id.message); 46 | BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); 47 | navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); 48 | 49 | mTextMessage.setOnClickListener(this); 50 | } 51 | 52 | @PermissionRequest( 53 | requestCode = 10010, 54 | requestPermissions = {Manifest.permission.ACCESS_FINE_LOCATION, 55 | Manifest.permission.ACCESS_COARSE_LOCATION, 56 | Manifest.permission.READ_CONTACTS} 57 | ,needReCall = true 58 | ) 59 | private void setText(final String text) { 60 | startActivity(new Intent(this,SecondActivity.class).putExtra("text",text)); 61 | } 62 | 63 | @Override 64 | public void onClick(View v) { 65 | setText("哈哈哈哈哈哈"); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/ykbjson/app/simplepermission/SecondActivity.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.app.simplepermission; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | public class SecondActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_second); 13 | getSupportFragmentManager().beginTransaction().replace(R.id.container, Fragment.instantiate(this, TestFragment.class.getName(), getIntent().getExtras())) 14 | .commit(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/ykbjson/app/simplepermission/TestApplication.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.app.simplepermission; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.support.multidex.MultiDex; 6 | 7 | /** 8 | * Description: 9 | * Creator:yankebin 10 | * CreatedAt:2018/10/29 11 | */ 12 | public class TestApplication extends Application { 13 | @Override 14 | protected void attachBaseContext(Context base) { 15 | super.attachBaseContext(base); 16 | MultiDex.install(base); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/ykbjson/app/simplepermission/TestFragment.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.app.simplepermission; 2 | 3 | import android.Manifest; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.TextView; 12 | import android.widget.Toast; 13 | 14 | import com.ykbjson.lib.simplepermission.ano.PermissionNotify; 15 | import com.ykbjson.lib.simplepermission.ano.PermissionRequest; 16 | 17 | /** 18 | * Description: 19 | * Creator:yankebin 20 | * CreatedAt:2018/11/8 21 | */ 22 | @PermissionNotify 23 | public class TestFragment extends Fragment { 24 | private TextView mTextMessage; 25 | 26 | 27 | @Nullable 28 | @Override 29 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 30 | View view = inflater.inflate(R.layout.fragment_test, container, false); 31 | mTextMessage = view.findViewById(R.id.message); 32 | return view; 33 | } 34 | 35 | @Override 36 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 37 | super.onViewCreated(view, savedInstanceState); 38 | mTextMessage.setText(getArguments().getString("text")); 39 | test(); 40 | } 41 | 42 | @PermissionRequest( 43 | requestCode = 100, 44 | requestPermissions = {Manifest.permission.READ_SMS}, 45 | needReCall = true) 46 | private void test() { 47 | Toast.makeText(getActivity(), "呵呵呵呵呵", Toast.LENGTH_LONG).show(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dashboard_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /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/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_second.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/menu/navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SimplePermission 3 | Home 4 | Dashboard 5 | Notifications 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/ykbjson/app/simplepermission/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.app.simplepermission; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from: "config.gradle" 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | mavenCentral() 9 | mavenLocal() 10 | maven {//本地Maven仓库地址 11 | url uri('/Users/yanan/Desktop/android_project/mavenlocal/') 12 | } 13 | maven { url 'https://dl.bintray.com/ykbjson/maven' } 14 | } 15 | dependencies { 16 | classpath 'com.android.tools.build:gradle:3.1.1' 17 | // classpath 'com.ykbjson.simplepermission:simplepermissionplugin:1.0.1' 18 | classpath 'com.novoda:bintray-release:0.8.1' 19 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' 20 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' 21 | // NOTE: Do not place your application dependencies here; they belong 22 | // in the individual module build.gradle files 23 | } 24 | } 25 | 26 | allprojects { 27 | repositories { 28 | google() 29 | jcenter() 30 | mavenCentral() 31 | mavenLocal() 32 | maven {//本地Maven仓库地址 33 | url uri('/Users/yanan/Desktop/android_project/mavenlocal/') 34 | } 35 | maven { url 'https://dl.bintray.com/ykbjson/maven' } 36 | } 37 | } 38 | 39 | task clean(type: Delete) { 40 | delete rootProject.buildDir 41 | } 42 | 43 | // 指定javadoc UTF-8格式 44 | task javadoc(type: Javadoc) { 45 | options{ 46 | encoding "UTF-8" 47 | charSet 'UTF-8' 48 | links "http://docs.oracle.com/javase/7/docs/api" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | plugins = [ 3 | library : 'com.android.library', 4 | application: 'com.android.application', 5 | maven : 'com.github.dcendents.android-maven', 6 | bintray : 'com.jfrog.bintray', 7 | apt : 'com.neenbedankt.android-apt' 8 | ] 9 | 10 | android = [ 11 | applicationId : 'com.ykbjson.app.simplepermission', 12 | compileSdkVersion: 27, 13 | buildToolsVersion: '27.0.3', 14 | 15 | minSdkVersion : 17, 16 | targetSdkVersion : 27, 17 | 18 | versionCode : 100, 19 | versionName : '1.0.0', 20 | ] 21 | 22 | bintray = [ 23 | plugVersion : '1.0.1', 24 | anoVersion : '1.0.0', 25 | libraryVersion: '1.0.1', 26 | 27 | siteUrl : 'https://github.com/ykbjson/SimplePermission', 28 | gitUrl : 'https://github.com/ykbjson/SimplePermission.git', 29 | 30 | group : 'com.ykbjson.simplepermission', 31 | 32 | // project 33 | packaging : 'aar', 34 | name : 'SimplePermission', 35 | description : 'SimplePermission For Android', 36 | 37 | // project.license 38 | licenseName : 'The Apache Software License, Version 2.0', 39 | licenseUrl : 'http://www.apache.org/licenses/LICENSE-2.0.txt', 40 | 41 | // project.developers 42 | developerId : 'ykbjson', 43 | developerName : 'ykbjson', 44 | developerEmail: 'ykbdevelop@gmail.com', 45 | 46 | // bintray 47 | binrayLibrary : "simplepermission", 48 | bintrayRepo : "maven", 49 | bintrayUser : 'ykbjson', 50 | bintrayLicense: "Apache-2.0" 51 | ] 52 | def rxBindingVersion = '2.1.1' 53 | def androidVersion = "27.0.2" 54 | dependencies = [ 55 | supportV4 : "com.android.support:support-v4:$androidVersion", 56 | appCompat : "com.android.support:appcompat-v7:$androidVersion", 57 | design : "com.android.support:design:$androidVersion", 58 | cardView : "com.android.support:cardview-v7:$androidVersion", 59 | recyclerView : "com.android.support:recyclerview-v7:$androidVersion", 60 | supportAnnotations : "com.android.support:support-annotations:$androidVersion", 61 | supportMultidex : 'com.android.support:multidex:1.0.1', 62 | 63 | loading : 'com.yanzhenjie:loading:1.0.2', 64 | swipeRecyclerView : 'com.yanzhenjie:recyclerview-swipe:1.1.3', 65 | 66 | RxBinding : "com.jakewharton.rxbinding2:rxbinding:$rxBindingVersion", 67 | RxBindingSupportV4 : "com.jakewharton.rxbinding2:rxbinding-support-v4:$rxBindingVersion", 68 | RxBindingAppcompatV7 : "com.jakewharton.rxbinding2:rxbinding-appcompat-v7:$rxBindingVersion", 69 | RxBindingRecycleViewV7: "com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:$rxBindingVersion", 70 | RxBindingDesign : "com.jakewharton.rxbinding2:rxbinding-design:$rxBindingVersion", 71 | RxBindingLeanBackv17 : "com.jakewharton.rxbinding2:rxbinding-leanback-v17:$rxBindingVersion", 72 | 73 | //rest+rxAndroid 74 | RXAndroid : 'io.reactivex.rxjava2:rxandroid:2.0.2', 75 | RXJava : 'io.reactivex.rxjava2:rxjava:2.1.11', 76 | // Retrofit2 77 | retrofit2 : 'com.squareup.retrofit2:retrofit:2.4.0', 78 | // Retrofit2适配RxJava 79 | retrofit2RXJavaAdapter: 'com.squareup.retrofit2:adapter-rxjava:2.4.0', 80 | // Retrofit2 Gson数据转换器 81 | retrofit2GsonConverter: 'com.squareup.retrofit2:converter-gson:2.4.0', 82 | // Retrofit2日志拦截器 83 | retrofit2Interceptor : 'com.squareup.okhttp3:logging-interceptor:3.10.0', 84 | 85 | glide : 'com.github.bumptech.glide:glide:3.7.0', 86 | //下拉刷新 87 | ultraptr : 'in.srain.cube:ultra-ptr:1.0.11', 88 | //ViewHelper 89 | nineoldandroids : 'com.nineoldandroids:library:2.4.0', 90 | //photoview 91 | photoview : 'com.github.chrisbanes.photoview:library:1.2.4', 92 | //圆角 93 | roundedimageview : 'com.makeramen:roundedimageview:2.2.1', 94 | //圆形图像 95 | circleimageview : 'de.hdodenhof:circleimageview:2.0.0', 96 | //上传图片 97 | upyun : 'com.upyun:upyun-android-sdk:2.0.4', 98 | //权限库 99 | permissiongen : 'com.lovedise:permissiongen:0.0.6', 100 | //图片选择器 101 | picasso : 'com.squareup.picasso:picasso:2.4.0', 102 | //okhttp-urlconnection 103 | okhttpUrlconnection : 'com.squareup.okhttp:okhttp-urlconnection:1.6.0', 104 | //bus 105 | xbus : 'com.mcxiaoke.xbus:bus:1.0.1', 106 | //sweetDialog 107 | sweetalert : 'cn.pedant.sweetalert:library:1.3', 108 | //OKHTTP 109 | okhttp3 : 'com.squareup.okhttp3:okhttp:3.10.0', 110 | //imageloader 111 | imageloader : 'com.nostra13.universalimageloader:universal-image-loader:1.9.5', 112 | //tablayout 113 | tablayout : 'com.flyco.tablayout:FlycoTabLayout_Lib:2.0.8@aar', 114 | //富文本查看器 115 | richtext : 'com.zzhoujay.richtext:richtext:2.3.7', 116 | //FlycoDialog 117 | FlycoDialog : 'com.flyco.dialog:FlycoDialog_Lib:1.2.6@aar', 118 | //阻尼效果 119 | overscroll : 'me.everything:overscroll-decor-android:1.0.1', 120 | //进度条 121 | RoundCornerProgressBar: 'com.akexorcist:RoundCornerProgressBar:2.0.3', 122 | //标签 123 | tagcloudview : 'com.github.kingideayou:tagcloudview:1.0.2', 124 | //热补丁修复框架tinker 125 | tinker : 'com.tencent.tinker:tinker-android-lib:1.9.1', 126 | tinkerAnno : 'com.tencent.tinker:tinker-android-anno:1.9.1', 127 | //growingio 128 | growingio : 'com.growingio.android:vds-android-agent:0.9.103@aar', 129 | //标签2 130 | androidtagview : 'co.lujun:androidtagview:1.0.6', 131 | //butterknife 132 | butterknife : 'com.jakewharton:butterknife:8.8.1', 133 | //图片选择器 134 | PictureSelector : 'com.github.LuckSiege.PictureSelector:picture_library:v2.1.0', 135 | 136 | 137 | fastjson : 'com.alibaba:fastjson:1.1.57.android', 138 | gson : 'com.google.code.gson:gson:2.8.2', 139 | 140 | ] 141 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx6656m 10 | android.useDexArchive= true 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javaertj/SimplePermission/acf72140da4d59eb5e0a6f2c2fad716ae7b858e3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /permissionplugin/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /permissionplugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | apply plugin: 'com.novoda.bintray-release' 3 | apply plugin: 'maven' 4 | 5 | //加载资源 6 | Properties properties = new Properties() 7 | InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream() 8 | properties.load(inputStream) 9 | 10 | publish { 11 | bintrayUser = properties.getProperty("bintrayUser") 12 | bintrayKey = properties.getProperty("bintrayKey") 13 | userOrg = 'ykbjson' // bintray注册的用户名 14 | groupId = rootProject.ext.bintray.group 15 | artifactId = 'simplepermissionplugin'// bintray创建的package 16 | publishVersion = rootProject.ext.bintray.plugVersion 17 | desc = 'SimplePermission权限申请库自动生成代码gradle插件' 18 | } 19 | 20 | compileGroovy { 21 | sourceCompatibility = 1.7 22 | targetCompatibility = 1.7 23 | } 24 | 25 | //group和version 26 | group=rootProject.ext.bintray.group 27 | version=rootProject.ext.bintray.plugVersion 28 | 29 | //打包到本地或者远程Maven库 30 | //uploadArchives { 31 | // repositories { 32 | // mavenDeployer { 33 | // //提交到远程服务器: 34 | // // repository(url: "http://www.xxx.com/repos") { 35 | // // authentication(userName: "admin", password: "admin") 36 | // // } 37 | // //本地的Maven地址设置为E:/Maven 38 | // repository(url: uri('/Users/yanan/Desktop/android_project/mavenlocal/')) 39 | // } 40 | // } 41 | //} 42 | 43 | dependencies { 44 | //gradle sdk 45 | implementation gradleApi() 46 | //groovy sdk 47 | implementation localGroovy() 48 | implementation project(':simplepermission_ano') 49 | // compile 'com.ykbjson.simplepermission:simplepermission_ano:1.0.0' 50 | implementation 'com.android.tools.build:gradle:3.1.1' 51 | implementation 'org.javassist:javassist:3.23.0-GA' 52 | } 53 | 54 | repositories { 55 | mavenCentral() 56 | } 57 | 58 | -------------------------------------------------------------------------------- /permissionplugin/src/main/groovy/com/ykbjson/lib/plugin/SimplePermissionPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.ykbjson.lib.plugin 2 | 3 | import com.android.build.gradle.AppExtension 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.Project 6 | 7 | public class SimplePermissionPlugin implements Plugin { 8 | 9 | @Override 10 | void apply(Project project) { 11 | System.out.println("======================================================") 12 | System.out.println("= welcome to simple permission gradle plugin! =") 13 | System.out.println("======================================================") 14 | //注册SimplePermissionTransform 15 | def android = project.extensions.findByType(AppExtension) 16 | def classTransform = new SimplePermissionTransform(project) 17 | android.registerTransform(classTransform) 18 | } 19 | } -------------------------------------------------------------------------------- /permissionplugin/src/main/groovy/com/ykbjson/lib/plugin/SimplePermissionPluginInject.groovy: -------------------------------------------------------------------------------- 1 | package com.ykbjson.lib.plugin 2 | 3 | import com.ykbjson.lib.simplepermission.ano.PermissionNotify 4 | import com.ykbjson.lib.simplepermission.ano.PermissionRequest 5 | import javassist.* 6 | import org.gradle.api.Project 7 | 8 | public class SimplePermissionPluginInject { 9 | 10 | private static final ClassPool pool = ClassPool.getDefault() 11 | 12 | private static final String PACKAGE_STAT = "com" 13 | private static final int CLASS_LENGTH = ".class".length() //6 14 | private static final String INJECT_PERMISSION_NOTIFY_METHOD_NAME = "onRequestPermissionsResult" 15 | private static 16 | final String INJECT_PERMISSION_REQUEST_METHOD_PARAMS_FIELD_NAME = "requestPermissionMethodParams" 17 | private static 18 | final String PERMISSIONS_MANAGER_PATH = "com.ykbjson.lib.simplepermission.PermissionsManager" 19 | private static 20 | final String PERMISSIONS_PATH = "com.ykbjson.lib.simplepermission.Permissions" 21 | private static 22 | final String PERMISSIONS_REQUEST_CALLBACK_PATH = "com.ykbjson.lib.simplepermission.PermissionsRequestCallback" 23 | private static 24 | final String PERMISSIONS_RESULT_ACTION_PATH = "com.ykbjson.lib.simplepermission.PermissionsResultAction" 25 | //添加成员变量存储需要请求权限的方法的参数信息 26 | private static 27 | final String REQUEST_PERMISSION_METHOD_PARAMS_INJECT_CONTENT = "private final Map requestPermissionMethodParams = new HashMap();" 28 | private static 29 | final String PERMISSIONS_REQUEST_CALLBACK_METHOD_ONGRANTED = "public void onGranted(int requestCode,String permission){ }\n" 30 | private static 31 | final String PERMISSIONS_REQUEST_CALLBACK_METHOD_ONDENIED = "public void onDenied(int requestCode,String permission){ }\n" 32 | private static 33 | final String PERMISSIONS_REQUEST_CALLBACK_METHOD_ONDENIED_FOREVER = "public void onDeniedForever(int requestCode,String permission){ }\n" 34 | private static 35 | final String PERMISSIONS_REQUEST_CALLBACK_METHOD_ONFAILURE = "public void onFailure(int requestCode,String[] deniedPermissions){ }\n" 36 | private static 37 | final String PERMISSIONS_REQUEST_CALLBACK_METHOD_ONSUCCESS = "public void onSuccess(int requestCode){ }\n" 38 | 39 | private static final String[] PERMISSIONS_REQUEST_CALLBACK_METHODS = [ 40 | PERMISSIONS_REQUEST_CALLBACK_METHOD_ONGRANTED, 41 | PERMISSIONS_REQUEST_CALLBACK_METHOD_ONDENIED, 42 | PERMISSIONS_REQUEST_CALLBACK_METHOD_ONDENIED_FOREVER, 43 | PERMISSIONS_REQUEST_CALLBACK_METHOD_ONFAILURE, 44 | PERMISSIONS_REQUEST_CALLBACK_METHOD_ONSUCCESS 45 | ] 46 | 47 | private static 48 | final String INJECT_PERMISSION_NOTIFY_CONTENT = "PermissionsManager.getInstance().notifyPermissionsChange(permissions,grantResults);\n" 49 | 50 | private static final String ACTIVITY_PATH = "android.app.Activity" 51 | 52 | private static final String FRAGMENT_PATH = "android.app.Fragment" 53 | 54 | private static final String V4_FRAGMENT_PATH = "android.support.v4.app.Fragment" 55 | 56 | static def classPathList = new ArrayList() 57 | 58 | public static void removeClassPath(Project project) { 59 | if (classPathList != null && !classPathList.isEmpty()) { 60 | classPathList.each { 61 | try { 62 | pool.removeClassPath(it) 63 | } catch (Exception e) { 64 | project.logger.error(e.getMessage()) 65 | } 66 | } 67 | classPathList.clear() 68 | } 69 | pool.clearImportedPackages() 70 | } 71 | 72 | public static void injectJar(Project project, String path, String packageName) { 73 | def classPath = new JarClassPath(path) 74 | pool.appendClassPath(classPath) 75 | classPathList.add(classPath) 76 | //project.android.bootClasspath 加入android.jar,否则找不到android相关的所有类 77 | pool.appendClassPath(project.android.bootClasspath[0].toString()) 78 | } 79 | 80 | public static void injectDir(Project project, String path, String packageName) { 81 | //将当前路径加入类池,不然找不到这个类 82 | pool.appendClassPath(path) 83 | //project.android.bootClasspath 加入android.jar,不然找不到android相关的所有类 84 | pool.appendClassPath(project.android.bootClasspath[0].toString()) 85 | //引入com.ykbjson.lib.simplepermission下面要使用到的类 86 | pool.importPackage(PERMISSIONS_MANAGER_PATH) 87 | pool.importPackage(PERMISSIONS_PATH) 88 | pool.importPackage(PERMISSIONS_RESULT_ACTION_PATH) 89 | pool.importPackage(PERMISSIONS_REQUEST_CALLBACK_PATH) 90 | pool.importPackage(ACTIVITY_PATH) 91 | pool.importPackage(FRAGMENT_PATH) 92 | pool.importPackage(V4_FRAGMENT_PATH) 93 | pool.importPackage("java.util.Map") 94 | pool.importPackage("java.util.HashMap") 95 | pool.importPackage("java.util.List") 96 | pool.importPackage("java.util.ArrayList") 97 | 98 | File dir = new File(path) 99 | if (!dir.isDirectory()) { 100 | return 101 | } 102 | dir.eachFileRecurse { File file -> 103 | String filePath = file.absolutePath 104 | //确保当前文件是class文件,并且不是系统自动生成的class文件 105 | if (!filePath.endsWith(".class") 106 | || filePath.contains('R$') 107 | || filePath.contains('R.class') 108 | || filePath.contains("BuildConfig.class")) { 109 | return 110 | } 111 | project.logger.warn "SimplePermission-----> filePath : " + filePath 112 | int index = filePath.indexOf(PACKAGE_STAT) 113 | int end = filePath.length() - CLASS_LENGTH // .class = 6 114 | String className = filePath.substring(index, end) 115 | .replace('\\', '.').replace('/', '.') 116 | project.logger.warn "SimplePermission-----> className : " + className 117 | if (null == className || "" == className || className.contains("\$")) { 118 | return 119 | } 120 | CtClass c = pool.getCtClass(className) 121 | //检验注解 122 | if (!c.hasAnnotation(PermissionNotify.class)) { 123 | return 124 | } 125 | //不是activity或fragment不处理 126 | if (!c.subclassOf(pool.get(ACTIVITY_PATH)) && !c.subclassOf(pool.get(FRAGMENT_PATH)) && !c.subclassOf(pool.get(V4_FRAGMENT_PATH))) { 127 | return 128 | } 129 | try { 130 | //找到需要修改的class,开始修改class文件,先解冻 131 | if (c.isFrozen()) { 132 | c.defrost() 133 | } 134 | //校验是否实现了PermissionsRequestCallback接口,没有实现则让其实现该接口,并重载相关方法 135 | if (null == c.getInterfaces() || !c.getInterfaces().contains(pool.get(PERMISSIONS_REQUEST_CALLBACK_PATH))) { 136 | c.addInterface(pool.get(PERMISSIONS_REQUEST_CALLBACK_PATH)) 137 | //重载方法 138 | for (String methodStr : PERMISSIONS_REQUEST_CALLBACK_METHODS) { 139 | project.logger.warn "SimplePermission-----> override PermissionsRequestCallback method : " + methodStr 140 | CtMethod overrideMethod = CtNewMethod.make(methodStr, c) 141 | c.addMethod(overrideMethod) 142 | } 143 | } 144 | //加入存储方法的map结构 145 | CtField requestPermissionMethodParamsField = findFieldByName(c, INJECT_PERMISSION_REQUEST_METHOD_PARAMS_FIELD_NAME) 146 | if (null == requestPermissionMethodParamsField) { 147 | requestPermissionMethodParamsField = CtField.make(REQUEST_PERMISSION_METHOD_PARAMS_INJECT_CONTENT, c) 148 | c.addField(requestPermissionMethodParamsField) 149 | } 150 | //检测Activity或Fragment是否声明或重写了onRequestPermissionsResult方法 151 | CtMethod notifyMethod = findMethodByName(c, INJECT_PERMISSION_NOTIFY_METHOD_NAME) 152 | //如果已经重写,则在super之前插入代码 153 | if (null != notifyMethod) { 154 | project.logger.error "SimplePermission-----> find notifyPermission method : " + notifyMethod.longName 155 | project.logger.error "SimplePermission-----> notifyPermission method insert content :\n" + INJECT_PERMISSION_NOTIFY_CONTENT 156 | notifyMethod.insertBefore(INJECT_PERMISSION_NOTIFY_CONTENT) 157 | } else { 158 | //没有重写,插入重载该方法的代码 159 | StringBuilder methodBuilder = new StringBuilder() 160 | .append("public void ") 161 | .append(INJECT_PERMISSION_NOTIFY_METHOD_NAME) 162 | .append("(") 163 | .append("int requestCode, ") 164 | .append("String[] permissions, ") 165 | .append("int[] grantResults) { \n") 166 | .append(INJECT_PERMISSION_NOTIFY_CONTENT) 167 | .append("super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n") 168 | .append("}") 169 | project.logger.error "SimplePermission-----> add notifyPermission method :\n " + methodBuilder.toString() 170 | 171 | notifyMethod = CtNewMethod.make(methodBuilder.toString(), c) 172 | c.addMethod(notifyMethod) 173 | } 174 | //在加了PermissionRequest注解的方法内插入权限请求的代码,根据PermissionRequest.needReCall来决定是否需要在权限回调成功的方法里插入代码 175 | for (CtMethod method : c.getDeclaredMethods()) { 176 | PermissionRequest permissionRequest = method.getAnnotation(PermissionRequest.class) 177 | //没有注解的方法忽略 178 | if (null == permissionRequest) { 179 | continue 180 | } 181 | project.logger.error "SimplePermission-----> method returnType : " + method.getReturnType().name 182 | //带返回值的方法暂时忽略 183 | if (!method.getReturnType().name.contains("void")) { 184 | continue 185 | } 186 | //静态方法忽略 187 | if (Modifier.isStatic(method.getModifiers())) { 188 | continue 189 | } 190 | project.logger.error "SimplePermission-----> find requestPermission method : " + method.longName 191 | //因为javassist不支持"{"与"}"有多行数据生成数组,所以要用一个String[] xx=new String[n]形式的数组把权限存储下来 192 | StringBuilder requestMethodBuilder = new StringBuilder() 193 | .append("String []requestPermissions = new String[") 194 | .append(permissionRequest.requestPermissions().length) 195 | .append("];\n") 196 | int eachIndex = 0 197 | for (String permission : permissionRequest.requestPermissions()) { 198 | requestMethodBuilder.append("requestPermissions[") 199 | .append(eachIndex) 200 | .append("] = \"") 201 | .append(permission) 202 | .append("\";\n") 203 | eachIndex++ 204 | } 205 | 206 | //因为前面已经排除了非Activity和Fragment的class 207 | boolean isActivity = c.subclassOf(pool.get(ACTIVITY_PATH)) 208 | requestMethodBuilder.append("final boolean hasPermissions = PermissionsManager.getInstance().hasAllPermissions(") 209 | // $0代码的是this,$1代表方法参数的第一个参数、$2代表方法参数的第二个参数,以此类推,$N代表是方法参数的第N个。 210 | .append("\$0") 211 | if (!isActivity) { 212 | requestMethodBuilder.append(".getActivity()") 213 | } 214 | requestMethodBuilder.append(",") 215 | .append("requestPermissions );\n") 216 | .append("if (!hasPermissions) {\n") 217 | //如果需要在权限申请成功后继续执行此方法的逻辑代码,则需要存储参数 218 | if (permissionRequest.needReCall()) { 219 | //开始存储方法参数 220 | CtClass[] mParameterTypes = method.getParameterTypes() 221 | //权限申请成功的方法插入代码 222 | CtMethod onSuccessMethod = c.getDeclaredMethod("onSuccess", CtClass.intType) 223 | if (null != onSuccessMethod) { 224 | StringBuilder onSuccessMethodBuilder = new StringBuilder("List params = requestPermissionMethodParams.get(Integer.valueOf(\$1));\n") 225 | .append("if(\$1==") 226 | .append(permissionRequest.requestCode()) 227 | .append("){\n") 228 | .append(method.getName()) 229 | .append("(") 230 | if (null != mParameterTypes && mParameterTypes.length > 0) { 231 | for (int k = 0; k < mParameterTypes.length; k++) { 232 | onSuccessMethodBuilder.append("(") 233 | .append(mParameterTypes[0].name) 234 | .append(")") 235 | .append("params.get(") 236 | .append(k) 237 | .append(")") 238 | if (k != mParameterTypes.length - 1) { 239 | onSuccessMethodBuilder.append(",") 240 | } 241 | } 242 | } 243 | onSuccessMethodBuilder.append() 244 | .append(");\n") 245 | .append("return;\n") 246 | .append("}\n") 247 | 248 | project.logger.error "SimplePermission-----> onSuccess method insert content :\n " + onSuccessMethodBuilder.toString() 249 | onSuccessMethod.insertBefore(onSuccessMethodBuilder.toString()) 250 | } 251 | //在申请权限的方法内部插入存储参数的代码 252 | if (null != mParameterTypes && mParameterTypes.length > 0) { 253 | requestMethodBuilder.append("List params = new java.util.ArrayList();\n") 254 | for (int k = 0; k < mParameterTypes.length; k++) { 255 | requestMethodBuilder.append("params.add(\$") 256 | .append(k + 1) 257 | .append(");\n") 258 | } 259 | requestMethodBuilder.append("requestPermissionMethodParams.put(") 260 | .append("Integer.valueOf(") 261 | .append(permissionRequest.requestCode()) 262 | .append(")") 263 | .append(",") 264 | .append("params);\n") 265 | } 266 | } 267 | requestMethodBuilder.append("PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(") 268 | .append(permissionRequest.requestCode())//requestCode 269 | .append(",") 270 | .append("\$0,") 271 | .append("requestPermissions,") 272 | .append("\$0);\n") 273 | //申请权限的方法的代码逻辑需要等到权限申请成功后才能执行的话,这里要在申请权限的代码后加上return,所以这个库目前有一个限制:带返回参数的方法暂不支持 274 | if (permissionRequest.needReCall()) { 275 | requestMethodBuilder.append("return;\n") 276 | } 277 | requestMethodBuilder.append("}\n") 278 | project.logger.error "SimplePermission-----> requestPermission method insert content :\n " + requestMethodBuilder.toString() 279 | method.insertBefore(requestMethodBuilder.toString()) 280 | } 281 | 282 | c.writeFile(path) 283 | c.detach() 284 | } catch (Exception e) { 285 | c.detach() 286 | e.printStackTrace() 287 | throw new RuntimeException(e) 288 | } 289 | } 290 | } 291 | 292 | static CtMethod findMethodByName(CtClass ctClass, String methodName) { 293 | //getDeclaredMethods获取自己申明的方法,c.getMethods()会把所有父类的方法都加上 294 | for (CtMethod method : ctClass.getDeclaredMethods()) { 295 | if (method.getLongName().contains(methodName)) { 296 | return method 297 | } 298 | } 299 | return null 300 | } 301 | 302 | static CtField findFieldByName(CtClass ctClass, String fieldName) { 303 | for (CtField field : ctClass.getDeclaredFields()) { 304 | if (field.getName().equals(fieldName)) { 305 | return field 306 | } 307 | } 308 | return null 309 | } 310 | } -------------------------------------------------------------------------------- /permissionplugin/src/main/groovy/com/ykbjson/lib/plugin/SimplePermissionTransform.groovy: -------------------------------------------------------------------------------- 1 | package com.ykbjson.lib.plugin 2 | 3 | import com.android.build.api.transform.* 4 | import com.android.build.gradle.internal.pipeline.TransformManager 5 | import org.apache.commons.codec.digest.DigestUtils 6 | import org.apache.commons.io.FileUtils 7 | import org.gradle.api.Project 8 | 9 | public class SimplePermissionTransform extends Transform { 10 | 11 | 12 | private Project mProject; 13 | 14 | SimplePermissionTransform(Project mProject) { 15 | this.mProject = mProject 16 | } 17 | 18 | // 设置我们自定义的Transform对应的Task名称 19 | // 类似:TransformClassesWithPreDexForXXX 20 | @Override 21 | String getName() { 22 | return "SimplePermissionTransform" 23 | } 24 | 25 | // 指定输入的类型,通过这里的设定,可以指定我们要处理的文件类型 26 | //这样确保其他类型的文件不会传入 27 | @Override 28 | Set getInputTypes() { 29 | return TransformManager.CONTENT_CLASS 30 | } 31 | 32 | // 指Transform要操作内容的范围,官方文档Scope有7种类型: 33 | // 34 | // EXTERNAL_LIBRARIES 只有外部库 35 | // PROJECT 只有项目内容 36 | // PROJECT_LOCAL_DEPS 只有项目的本地依赖(本地jar) 37 | // PROVIDED_ONLY 只提供本地或远程依赖项 38 | // SUB_PROJECTS 只有子项目。 39 | // SUB_PROJECTS_LOCAL_DEPS 只有子项目的本地依赖项(本地jar)。 40 | // TESTED_CODE 由当前变量(包括依赖项)测试的代码 41 | @Override 42 | Set getScopes() { 43 | return TransformManager.SCOPE_FULL_PROJECT 44 | } 45 | 46 | @Override 47 | boolean isIncremental() { 48 | return false 49 | } 50 | 51 | /** 52 | * 具体的处理 53 | */ 54 | @Override 55 | void transform(Context context, Collection inputs, 56 | Collection referencedInputs, 57 | TransformOutputProvider outputProvider, boolean isIncremental) 58 | throws IOException, TransformException, InterruptedException { 59 | mProject.logger.error "==============SimplePermission transform start==============" 60 | try { 61 | // Transform的inputs有两种类型,一种是目录,一种是jar包,要分开遍历 62 | inputs.each { TransformInput input -> 63 | //对类型为“文件夹”的input进行遍历 64 | input.directoryInputs.each { DirectoryInput directoryInput -> 65 | //文件夹里面包含的是我们手写的类以及R.class、BuildConfig.class以及R$XXX.class等 66 | // String packageName=project.extensions.findByName("applicationId") 67 | SimplePermissionPluginInject.injectDir(mProject, directoryInput.file.absolutePath, "com/ykbjson/app/simplepermission") 68 | // 获取output目录 69 | def dest = outputProvider.getContentLocation(directoryInput.name, 70 | directoryInput.contentTypes, directoryInput.scopes, 71 | Format.DIRECTORY) 72 | 73 | // 将input的目录复制到output指定目录 74 | FileUtils.copyDirectory(directoryInput.file, dest) 75 | } 76 | //对类型为jar文件的input进行遍历 77 | input.jarInputs.each { JarInput jarInput -> 78 | //jar文件一般是第三方依赖库jar文件 79 | SimplePermissionPluginInject.injectJar(mProject, jarInput.file.absolutePath, "com/ykbjson/app/simplepermission") 80 | // 重命名输出文件(同目录copyFile会冲突) 81 | def jarName = jarInput.name 82 | def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath()) 83 | if (jarName.endsWith(".jar")) { 84 | jarName = jarName.substring(0, jarName.length() - 4) 85 | } 86 | //生成输出路径 87 | def dest = outputProvider.getContentLocation(jarName + md5Name, 88 | jarInput.contentTypes, jarInput.scopes, Format.JAR) 89 | //将输入内容复制到输出 90 | FileUtils.copyFile(jarInput.file, dest) 91 | } 92 | } 93 | 94 | } catch (Exception e) { 95 | SimplePermissionPluginInject.removeClassPath(mProject) 96 | throw new RuntimeException(e) 97 | } 98 | SimplePermissionPluginInject.removeClassPath(mProject) 99 | mProject.logger.error "==============SimplePermission transform end==============" 100 | } 101 | } -------------------------------------------------------------------------------- /permissionplugin/src/main/resources/META-INF/gradle-plugins/com.ykbjson.simplepermission.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.ykbjson.lib.plugin.SimplePermissionPlugin -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':simplepermission', ':permissionplugin', ':simplepermission_ano' 2 | -------------------------------------------------------------------------------- /simplepermission/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /simplepermission/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | apply plugin: 'maven' 4 | apply plugin: 'com.github.dcendents.android-maven' 5 | 6 | 7 | //加载资源 8 | Properties properties = new Properties() 9 | InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream(); 10 | properties.load(inputStream) 11 | 12 | publish { 13 | bintrayUser = properties.getProperty("bintrayUser") 14 | bintrayKey = properties.getProperty("bintrayKey") 15 | userOrg = 'ykbjson' // bintray注册的用户名 16 | groupId = rootProject.ext.bintray.group 17 | artifactId = 'simplepermission'// bintray创建的package 18 | publishVersion = rootProject.ext.bintray.libraryVersion 19 | desc = 'SimplePermission权限申请库核心代码' 20 | } 21 | 22 | //group和version 23 | group='com.github.ykbjson' 24 | //version=rootProject.ext.bintray.plugVersion 25 | 26 | android { 27 | compileSdkVersion rootProject.ext.android.compileSdkVersion 28 | 29 | defaultConfig { 30 | minSdkVersion rootProject.ext.android.minSdkVersion 31 | targetSdkVersion rootProject.ext.android.targetSdkVersion 32 | versionCode rootProject.ext.android.versionCode 33 | versionName rootProject.ext.android.versionName 34 | } 35 | lintOptions { 36 | abortOnError false 37 | } 38 | } 39 | 40 | dependencies { 41 | implementation fileTree(dir: 'libs', include: ['*.jar']) 42 | implementation rootProject.ext.dependencies.appCompat 43 | implementation rootProject.ext.dependencies.RXAndroid 44 | implementation rootProject.ext.dependencies.RXJava 45 | } 46 | -------------------------------------------------------------------------------- /simplepermission/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 | -------------------------------------------------------------------------------- /simplepermission/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /simplepermission/src/main/java/com/ykbjson/lib/simplepermission/Permissions.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.lib.simplepermission; 2 | 3 | /** 4 | * Desription:Enum class to handle the different states 5 | * of permissions since the PackageManager only 6 | * has a granted and denied state. 7 | * Creator:yankebin 8 | * CreatedAt:2018/11/7 9 | */ 10 | enum Permissions { 11 | GRANTED, 12 | DENIED, 13 | NOT_FOUND, 14 | USER_DENIED_FOREVER 15 | } -------------------------------------------------------------------------------- /simplepermission/src/main/java/com/ykbjson/lib/simplepermission/PermissionsManager.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.lib.simplepermission; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.pm.PackageInfo; 7 | import android.content.pm.PackageManager; 8 | import android.os.Build; 9 | import android.support.annotation.NonNull; 10 | import android.support.annotation.Nullable; 11 | import android.support.v4.app.ActivityCompat; 12 | import android.support.v4.app.Fragment; 13 | import android.support.v4.content.PermissionChecker; 14 | import android.util.Log; 15 | 16 | import java.lang.reflect.Field; 17 | import java.util.ArrayList; 18 | import java.util.HashSet; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | import java.util.Set; 22 | 23 | /** 24 | * Desription:A class to help you manage your permissions simply. 25 | * Creator:yankebin 26 | * CreatedAt:2018/11/7 27 | */ 28 | public class PermissionsManager { 29 | private final String TAG = getClass().getName(); 30 | private final Set mPendingRequests = new HashSet<>(1); 31 | private final Set mPermissions = new HashSet<>(1); 32 | private final List mPendingActions = new ArrayList<>(1); 33 | 34 | private boolean enableLog; 35 | private static volatile PermissionsManager mInstance = null; 36 | 37 | public static PermissionsManager getInstance() { 38 | if (null == mInstance) { 39 | synchronized (PermissionsManager.class) { 40 | if (mInstance == null) { 41 | mInstance = new PermissionsManager(); 42 | } 43 | } 44 | } 45 | return mInstance; 46 | } 47 | 48 | private PermissionsManager() { 49 | initializePermissionsMap(); 50 | } 51 | 52 | /** 53 | * This method uses reflection to read all the permissions in the Manifest class. 54 | * This is necessary because some permissions do not exist on older versions of Android, 55 | * since they do not exist, they will be denied when you check whether you have permission 56 | * which is problematic since a new permission is often added where there was no previous 57 | * permission required. We initialize a Set of available permissions and check the set 58 | * when checking if we have permission since we want to know when we are denied a permission 59 | * because it doesn't exist yet. 60 | */ 61 | private synchronized void initializePermissionsMap() { 62 | Field[] fields = Manifest.permission.class.getFields(); 63 | for (Field field : fields) { 64 | String name = null; 65 | try { 66 | name = (String) field.get(""); 67 | } catch (IllegalAccessException e) { 68 | if (enableLog) { 69 | Log.d(TAG, "Could not access field", e); 70 | } 71 | } 72 | mPermissions.add(name); 73 | } 74 | } 75 | 76 | /** 77 | * This method retrieves all the permissions declared in the application's manifest. 78 | * It returns a non null array of permisions that can be declared. 79 | * 80 | * @param activity the Activity necessary to check what permissions we have. 81 | * @return a non null array of permissions that are declared in the application manifest. 82 | */ 83 | @NonNull 84 | public synchronized String[] getManifestPermissions(@NonNull final Activity activity) { 85 | PackageInfo packageInfo = null; 86 | List list = new ArrayList<>(1); 87 | try { 88 | if (enableLog) { 89 | Log.d(TAG, activity.getPackageName()); 90 | } 91 | packageInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS); 92 | } catch (PackageManager.NameNotFoundException e) { 93 | if (enableLog) { 94 | Log.d(TAG, "A problem occurred when retrieving permissions", e); 95 | } 96 | } 97 | if (packageInfo != null) { 98 | String[] permissions = packageInfo.requestedPermissions; 99 | if (permissions != null) { 100 | for (String perm : permissions) { 101 | if (enableLog) { 102 | Log.d(TAG, "Manifest contained permission: " + perm); 103 | } 104 | list.add(perm); 105 | 106 | } 107 | } 108 | } 109 | return list.toArray(new String[list.size()]); 110 | } 111 | 112 | /** 113 | * This method adds the {@link PermissionsResultAction} to the current list 114 | * of pending actions that will be completed when the permissions are 115 | * received. The list of permissions passed to this method are registered 116 | * in the PermissionsResultAction object so that it will be notified of changes 117 | * made to these permissions. 118 | * 119 | * @param permissions the required permissions for the action to be executed. 120 | * @param action the action to add to the current list of pending actions. 121 | */ 122 | private synchronized void addPendingAction(@NonNull String[] permissions, 123 | @Nullable PermissionsResultAction action) { 124 | if (action == null) { 125 | return; 126 | } 127 | action.registerPermissions(permissions); 128 | mPendingActions.add(action); 129 | } 130 | 131 | /** 132 | * This method removes a pending action from the list of pending actions. 133 | * It is used for cases where the permission has already been granted, so 134 | * you immediately wish to remove the pending action from the queue and 135 | * execute the action. 136 | * 137 | * @param action the action to remove 138 | */ 139 | private synchronized void removePendingAction(@Nullable PermissionsResultAction action) { 140 | for (Iterator iterator = mPendingActions.iterator(); 141 | iterator.hasNext(); ) { 142 | PermissionsResultAction weakRef = iterator.next(); 143 | if (weakRef == action || weakRef == null) { 144 | iterator.remove(); 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * This static method can be used to check whether or not you have a specific permission. 151 | * It is basically a less verbose method of using {@link ActivityCompat#checkSelfPermission(Context, String)} 152 | * and will simply return a boolean whether or not you have the permission. If you pass 153 | * in a null Context object, it will return false as otherwise it cannot check the permission. 154 | * However, the Activity parameter is nullable so that you can pass in a reference that you 155 | * are not always sure will be valid or not (e.g. getActivity() from Fragment). 156 | * 157 | * @param context the Context necessary to check the permission 158 | * @param permission the permission to check 159 | * @return true if you have been granted the permission, false otherwise 160 | */ 161 | public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) { 162 | return context != null && (PermissionChecker.checkSelfPermission(context, permission) 163 | == PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission)); 164 | } 165 | 166 | /** 167 | * This static method can be used to check whether or not you have several specific permissions. 168 | * It is simpler than checking using {@link ActivityCompat#checkSelfPermission(Context, String)} 169 | * for each permission and will simply return a boolean whether or not you have all the permissions. 170 | * If you pass in a null Context object, it will return false as otherwise it cannot check the 171 | * permission. However, the Activity parameter is nullable so that you can pass in a reference 172 | * that you are not always sure will be valid or not (e.g. getActivity() from Fragment). 173 | * 174 | * @param context the Context necessary to check the permission 175 | * @param permissions the permissions to check 176 | * @return true if you have been granted all the permissions, false otherwise 177 | */ 178 | public synchronized boolean hasAllPermissions(@Nullable Context context, @NonNull String[] permissions) { 179 | if (context == null) { 180 | return false; 181 | } 182 | boolean hasAllPermissions = true; 183 | for (String perm : permissions) { 184 | hasAllPermissions &= hasPermission(context, perm); 185 | } 186 | return hasAllPermissions; 187 | } 188 | 189 | /** 190 | * This static method can be used to check whether or not you have several specific permissions. 191 | * It is simpler than checking using {@link ActivityCompat#checkSelfPermission(Context, String)} 192 | * for each permission and will simply return a boolean whether or not you have all the permissions. 193 | * If you pass in a null Context object, it will return false as otherwise it cannot check the 194 | * permission. However, the Activity parameter is nullable so that you can pass in a reference 195 | * that you are not always sure will be valid or not (e.g. getActivity() from Fragment). 196 | * 197 | * @param context the Context necessary to check the permission 198 | * @return true if you have been granted all the permissions, false otherwise 199 | */ 200 | public synchronized boolean hasAllPermissions(@Nullable Activity context) { 201 | if (context == null) { 202 | return false; 203 | } 204 | String[] perms = getManifestPermissions(context); 205 | return hasAllPermissions(context, perms); 206 | } 207 | 208 | /** 209 | * This method will request all the permissions declared in your application manifest 210 | * for the specified {@link PermissionsResultAction}. The purpose of this method is to enable 211 | * all permissions to be requested at one shot. The PermissionsResultAction is used to notify 212 | * you of the user allowing or denying each permission. The Activity and PermissionsResultAction 213 | * parameters are both annotated Nullable, but this method will not work if the Activity 214 | * is null. It is only annotated Nullable as a courtesy to prevent crashes in the case 215 | * that you call this from a Fragment where {@link Fragment#getActivity()} could yield 216 | * null. Additionally, you will not receive any notification of permissions being granted 217 | * if you provide a null PermissionsResultAction. 218 | * 219 | * @param requestCode the permissions requestCode 220 | * @param activity the Activity necessary to request and check permissions. 221 | * @param callback the PermissionsRequestCallback used to notify you of permissions being accepted. 222 | */ 223 | public synchronized void requestAllManifestPermissionsIfNecessary(int requestCode, @Nullable Activity activity, 224 | @Nullable PermissionsRequestCallback callback) { 225 | if (activity == null) { 226 | return; 227 | } 228 | String[] perms = getManifestPermissions(activity); 229 | requestPermissionsIfNecessaryForResult(requestCode, activity, perms, callback); 230 | } 231 | 232 | /** 233 | * This method should be used to execute a {@link PermissionsResultAction} for the array 234 | * of permissions passed to this method. This method will request the permissions if 235 | * they need to be requested (i.e. we don't have permission yet) and will add the 236 | * PermissionsResultAction to the queue to be notified of permissions being granted or 237 | * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately. 238 | * The Activity variable is nullable, but if it is null, the method will fail to execute. 239 | * This is only nullable as a courtesy for Fragments where getActivity() may yeild null 240 | * if the Fragment is not currently added to its parent Activity. 241 | * 242 | * @param requestCode the permissions requestCode 243 | * @param activity the activity necessary to request the permissions. 244 | * @param permissions the list of permissions to request for the {@link PermissionsResultAction}. 245 | * @param callback the PermissionsRequestCallback to notify when the permissions are granted or denied. 246 | */ 247 | public synchronized void requestPermissionsIfNecessaryForResult(int requestCode, @Nullable Activity activity, 248 | @NonNull String[] permissions, 249 | @Nullable PermissionsRequestCallback callback) { 250 | if (activity == null) { 251 | return; 252 | } 253 | final PermissionsResultAction action = new PermissionsResultAction(requestCode, callback); 254 | addPendingAction(permissions, action); 255 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 256 | doPermissionWorkBeforeAndroidM(activity, permissions, action); 257 | } else { 258 | List permList = getPermissionsListToRequest(activity, permissions, action); 259 | if (permList.isEmpty()) { 260 | //if there is no permission to request, there is no reason to keep the action in the list 261 | removePendingAction(action); 262 | } else { 263 | String[] permsToRequest = permList.toArray(new String[permList.size()]); 264 | mPendingRequests.addAll(permList); 265 | ActivityCompat.requestPermissions(activity, permsToRequest, 1); 266 | } 267 | } 268 | } 269 | 270 | /** 271 | * This method should be used to execute a {@link PermissionsResultAction} for the array 272 | * of permissions passed to this method. This method will request the permissions if 273 | * they need to be requested (i.e. we don't have permission yet) and will add the 274 | * PermissionsResultAction to the queue to be notified of permissions being granted or 275 | * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately. 276 | * The Fragment variable is used, but if {@link Fragment#getActivity()} returns null, this method 277 | * will fail to work as the activity reference is necessary to check for permissions. 278 | * 279 | * @param requestCode the permissions requestCode 280 | * @param fragment the fragment necessary to request the permissions. 281 | * @param permissions the list of permissions to request for the {@link PermissionsResultAction}. 282 | * @param callback the PermissionsRequestCallback to notify when the permissions are granted or denied. 283 | */ 284 | public synchronized void requestPermissionsIfNecessaryForResult(int requestCode, @NonNull Fragment fragment, 285 | @NonNull String[] permissions, 286 | @Nullable PermissionsRequestCallback callback) { 287 | Activity activity = fragment.getActivity(); 288 | if (activity == null) { 289 | return; 290 | } 291 | final PermissionsResultAction action = new PermissionsResultAction(requestCode, callback); 292 | addPendingAction(permissions, action); 293 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 294 | doPermissionWorkBeforeAndroidM(activity, permissions, action); 295 | } else { 296 | List permList = getPermissionsListToRequest(activity, permissions, action); 297 | if (permList.isEmpty()) { 298 | //if there is no permission to request, there is no reason to keep the action int the list 299 | removePendingAction(action); 300 | } else { 301 | String[] permsToRequest = permList.toArray(new String[permList.size()]); 302 | mPendingRequests.addAll(permList); 303 | fragment.requestPermissions(permsToRequest, 1); 304 | } 305 | } 306 | } 307 | 308 | 309 | /** 310 | * This method should be used to execute a {@link PermissionsResultAction} for the array 311 | * of permissions passed to this method. This method will request the permissions if 312 | * they need to be requested (i.e. we don't have permission yet) and will add the 313 | * PermissionsResultAction to the queue to be notified of permissions being granted or 314 | * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately. 315 | * The Fragment variable is used, but if {@link android.app.Fragment#getActivity()} returns null, this method 316 | * will fail to work as the activity reference is necessary to check for permissions. 317 | * 318 | * @param requestCode the permissions requestCode 319 | * @param fragment the android.app.fragment necessary to request the permissions. 320 | * @param permissions the list of permissions to request for the {@link PermissionsResultAction}. 321 | * @param callback the PermissionsRequestCallback to notify when the permissions are granted or denied. 322 | */ 323 | public synchronized void requestPermissionsIfNecessaryForResult(int requestCode, @NonNull android.app.Fragment fragment, 324 | @NonNull String[] permissions, 325 | @Nullable PermissionsRequestCallback callback) { 326 | Activity activity = fragment.getActivity(); 327 | if (activity == null) { 328 | return; 329 | } 330 | final PermissionsResultAction action = new PermissionsResultAction(requestCode, callback); 331 | addPendingAction(permissions, action); 332 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 333 | doPermissionWorkBeforeAndroidM(activity, permissions, action); 334 | } else { 335 | List permList = getPermissionsListToRequest(activity, permissions, action); 336 | if (permList.isEmpty()) { 337 | //if there is no permission to request, there is no reason to keep the action int the list 338 | removePendingAction(action); 339 | } else { 340 | String[] permsToRequest = permList.toArray(new String[permList.size()]); 341 | mPendingRequests.addAll(permList); 342 | fragment.requestPermissions(permsToRequest, 1); 343 | } 344 | } 345 | } 346 | 347 | /** 348 | * This method notifies the PermissionsManager that the permissions have change. If you are making 349 | * the permissions requests using an Activity, then this method should be called from the 350 | * Activity callback onRequestPermissionsResult() with the variables passed to that method. If 351 | * you are passing a Fragment to make the permissions request, then you should call this in 352 | * the {@link Fragment#onRequestPermissionsResult(int, String[], int[])} method. 353 | * It will notify all the pending PermissionsResultAction objects currently 354 | * in the queue, and will remove the permissions request from the list of pending requests. 355 | * 356 | * @param permissions the permissions that have changed. 357 | * @param results the values for each permission. 358 | */ 359 | public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) { 360 | int size = permissions.length; 361 | if (results.length < size) { 362 | size = results.length; 363 | } 364 | Iterator iterator = mPendingActions.iterator(); 365 | while (iterator.hasNext()) { 366 | PermissionsResultAction action = iterator.next(); 367 | for (int n = 0; n < size; n++) { 368 | if (action == null || action.onResult(permissions[n], results[n])) { 369 | iterator.remove(); 370 | break; 371 | } 372 | } 373 | } 374 | for (int n = 0; n < size; n++) { 375 | mPendingRequests.remove(permissions[n]); 376 | } 377 | } 378 | 379 | /** 380 | * When request permissions on devices before Android M (Android 6.0, API Level 23) 381 | * Do the granted or denied work directly according to the permission status 382 | * 383 | * @param activity the activity to check permissions 384 | * @param permissions the permissions names 385 | * @param action the callback work object, containing what we what to do after 386 | * permission check 387 | */ 388 | private void doPermissionWorkBeforeAndroidM(@NonNull Activity activity, 389 | @NonNull String[] permissions, 390 | @Nullable PermissionsResultAction action) { 391 | for (String perm : permissions) { 392 | if (action != null) { 393 | if (!mPermissions.contains(perm)) { 394 | action.onResult(perm, Permissions.NOT_FOUND); 395 | } else if (ActivityCompat.checkSelfPermission(activity, perm) 396 | != PackageManager.PERMISSION_GRANTED) { 397 | action.onResult(perm, Permissions.DENIED); 398 | } else { 399 | action.onResult(perm, Permissions.GRANTED); 400 | } 401 | } 402 | } 403 | } 404 | 405 | /** 406 | * Filter the permissions list: 407 | * If a permission is not granted, add it to the result list 408 | * if a permission is granted, do the granted work, do not add it to the result list 409 | * 410 | * @param activity the activity to check permissions 411 | * @param permissions all the permissions names 412 | * @param action the callback work object, containing what we what to do after 413 | * permission check 414 | * @return a list of permissions names that are not granted yet 415 | */ 416 | @NonNull 417 | private List getPermissionsListToRequest(@NonNull Activity activity, 418 | @NonNull String[] permissions, 419 | @Nullable PermissionsResultAction action) { 420 | List permList = new ArrayList<>(permissions.length); 421 | for (String perm : permissions) { 422 | if (!mPermissions.contains(perm)) { 423 | if (action != null) { 424 | action.onResult(perm, Permissions.NOT_FOUND); 425 | } 426 | } else if (PermissionChecker.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) { 427 | if (ActivityCompat.shouldShowRequestPermissionRationale(activity, perm)) { 428 | if (action != null) { 429 | action.onResult(perm, Permissions.USER_DENIED_FOREVER); 430 | } 431 | } else if (!mPendingRequests.contains(perm)) { 432 | permList.add(perm); 433 | } 434 | } else { 435 | if (action != null) { 436 | action.onResult(perm, Permissions.GRANTED); 437 | } 438 | } 439 | } 440 | return permList; 441 | } 442 | 443 | /** 444 | * Set Log status 445 | * 446 | * @param enableLog Log status value 447 | */ 448 | public void setEnableLog(boolean enableLog) { 449 | this.enableLog = enableLog; 450 | } 451 | 452 | /** 453 | * return Log status 454 | * 455 | * @return Log status 456 | */ 457 | public boolean isEnableLog() { 458 | return enableLog; 459 | } 460 | } -------------------------------------------------------------------------------- /simplepermission/src/main/java/com/ykbjson/lib/simplepermission/PermissionsRequestCallback.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.lib.simplepermission; 2 | 3 | /** 4 | * Description:权限申请结果回调 5 | * Creator:yankebin 6 | * CreatedAt:2018/11/1 7 | */ 8 | public interface PermissionsRequestCallback { 9 | /** 10 | * This method is called when a permission that have been 11 | * requested have been granted by the user. In this method 12 | * you should put your permission(s) sensitive code that can 13 | * only be executed with the required permissions. 14 | */ 15 | void onGranted(int requestCode, String permission); 16 | 17 | /** 18 | * This method is called when a permission has been denied by 19 | * the user. It provides you with the permission that was denied 20 | * and will be executed on the Looper you pass to the constructor 21 | * of this class, or the Looper that this object was created on. 22 | * 23 | * @param permission the permission that was denied. 24 | */ 25 | void onDenied(int requestCode, String permission); 26 | 27 | /** 28 | * This method is called when a permission has been denied by 29 | * the user forever. It provides you with the permission that was denied 30 | * and will be executed on the Looper you pass to the constructor 31 | * of this class, or the Looper that this object was created on. 32 | * 33 | * @param permission the permission that was denied. 34 | */ 35 | void onDeniedForever(int requestCode, String permission); 36 | 37 | /** 38 | * This method is called when all permissions has been check complete 39 | * but some permissions denied. 40 | * 41 | * @param deniedPermissions those denied permissions 42 | */ 43 | void onFailure(int requestCode, String[] deniedPermissions); 44 | 45 | /** 46 | * This method is called when all permissions has been check complete 47 | * and all permissions granted. 48 | */ 49 | void onSuccess(int requestCode); 50 | } 51 | -------------------------------------------------------------------------------- /simplepermission/src/main/java/com/ykbjson/lib/simplepermission/PermissionsResultAction.java: -------------------------------------------------------------------------------- 1 | 2 | package com.ykbjson.lib.simplepermission; 3 | 4 | import android.content.pm.PackageManager; 5 | import android.os.Looper; 6 | import android.support.annotation.CallSuper; 7 | import android.support.annotation.NonNull; 8 | import android.support.annotation.Nullable; 9 | import android.util.Log; 10 | 11 | import java.util.Collections; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | 15 | import io.reactivex.Scheduler; 16 | import io.reactivex.android.schedulers.AndroidSchedulers; 17 | 18 | /** 19 | * Desription:

This abstract class should be used to create an if/else action that the PermissionsManager 20 | * can execute when the permissions you request are granted or denied. Simple use involves 21 | * creating an anonymous instance of it and passing that instance to the 22 | * requestPermissionsIfNecessaryForResult method. The result will be sent back to you as 23 | * either onGranted (all permissions have been granted), or onDenied (a required permission 24 | * has been denied). Ideally you put your functionality in the onGranted method and notify 25 | * the user what won't work in the onDenied method.

26 | * Creator:yankebin 27 | * CreatedAt:2018/11/7 28 | */ 29 | public class PermissionsResultAction { 30 | private final String TAG = getClass().getName(); 31 | 32 | private Looper mLooper = Looper.getMainLooper(); 33 | private final Set mPermissions = new HashSet<>(1); 34 | private final Set mDeniedPermissions = new HashSet<>(1); 35 | 36 | private final PermissionsRequestCallback mPermissionsRequestCallback; 37 | private final int mRequestCode; 38 | 39 | /** 40 | * Default Constructor 41 | */ 42 | public PermissionsResultAction(int requestCode, @Nullable PermissionsRequestCallback permissionsRequestCallback) { 43 | mRequestCode = requestCode; 44 | mPermissionsRequestCallback = permissionsRequestCallback; 45 | } 46 | 47 | /** 48 | * Alternate Constructor. Pass the looper you wish the PermissionsResultAction 49 | * callbacks to be executed on if it is not the current Looper. For instance, 50 | * if you are making a permissions request from a background thread but wish the 51 | * callback to be on the UI thread, use this constructor to specify the UI Looper. 52 | * 53 | * @param looper the looper that the callbacks will be called using. 54 | */ 55 | public PermissionsResultAction(int requestCode, @Nullable PermissionsRequestCallback permissionsRequestCallback, @NonNull Looper looper) { 56 | this(requestCode, permissionsRequestCallback); 57 | mLooper = looper; 58 | } 59 | 60 | /** 61 | * This method is called when a permission that have been 62 | * requested have been granted by the user. In this method 63 | * you should put your permission(s) sensitive code that can 64 | * only be executed with the required permissions. 65 | */ 66 | public void onGranted(String permission) { 67 | if (null != mPermissionsRequestCallback) { 68 | mPermissionsRequestCallback.onGranted(mRequestCode, permission); 69 | } 70 | } 71 | 72 | /** 73 | * This method is called when a permission has been denied by 74 | * the user. It provides you with the permission that was denied 75 | * and will be executed on the Looper you pass to the constructor 76 | * of this class, or the Looper that this object was created on. 77 | * 78 | * @param permission the permission that was denied. 79 | */ 80 | public void onDenied(String permission) { 81 | if (null != mPermissionsRequestCallback) { 82 | mPermissionsRequestCallback.onDenied(mRequestCode, permission); 83 | } 84 | } 85 | 86 | /** 87 | * This method is called when a permission has been denied by 88 | * the user forever. It provides you with the permission that was denied 89 | * and will be executed on the Looper you pass to the constructor 90 | * of this class, or the Looper that this object was created on. 91 | * 92 | * @param permission the permission that was denied. 93 | */ 94 | public void onDeniedForever(String permission) { 95 | if (null != mPermissionsRequestCallback) { 96 | mPermissionsRequestCallback.onDeniedForever(mRequestCode, permission); 97 | } 98 | } 99 | 100 | /** 101 | * This method is called when all permissions has been check complete 102 | * but some permissions denied. 103 | * 104 | * @param deniedPermissions those denied permissions 105 | */ 106 | public void onFailure(String[] deniedPermissions) { 107 | if (null != mPermissionsRequestCallback) { 108 | mPermissionsRequestCallback.onFailure(mRequestCode, deniedPermissions); 109 | } 110 | } 111 | 112 | /** 113 | * This method is called when all permissions has been check complete 114 | * and all permissions granted. 115 | */ 116 | public void onSuccess() { 117 | if (null != mPermissionsRequestCallback) { 118 | mPermissionsRequestCallback.onSuccess(mRequestCode); 119 | } 120 | } 121 | 122 | /** 123 | * This method is used to determine if a permission not 124 | * being present on the current Android platform should 125 | * affect whether the PermissionsResultAction should continue 126 | * listening for events. By default, it returns true and will 127 | * simply ignore the permission that did not exist. Usually this will 128 | * work fine since most new permissions are introduced to 129 | * restrict what was previously allowed without permission. 130 | * If that is not the case for your particular permission you 131 | * request, override this method and return false to result in the 132 | * Action being denied. 133 | * 134 | * @param permission the permission that doesn't exist on this 135 | * Android version 136 | * @return return true if the PermissionsResultAction should 137 | * ignore the lack of the permission and proceed with exection 138 | * or false if the PermissionsResultAction should treat the 139 | * absence of the permission on the API level as a denial. 140 | */ 141 | public synchronized boolean shouldIgnorePermissionNotFound(String permission) { 142 | if (PermissionsManager.getInstance().isEnableLog()) { 143 | Log.d(TAG, "Permission not found: " + permission); 144 | } 145 | return true; 146 | } 147 | 148 | 149 | @CallSuper 150 | protected synchronized final boolean onResult(final @NonNull String permission, int result) { 151 | if (result == PackageManager.PERMISSION_GRANTED) { 152 | return onResult(permission, Permissions.GRANTED); 153 | } else { 154 | return onResult(permission, Permissions.DENIED); 155 | } 156 | } 157 | 158 | /** 159 | * This method is called when a particular permission has changed. 160 | * This method will be called for all permissions, so this method determines 161 | * if the permission affects the state or not and whether it can proceed with 162 | * calling onGranted or if onDenied should be called. 163 | * 164 | * @param permission the permission that changed. 165 | * @param result the result for that permission. 166 | * @return this method returns true if its primary action has been completed 167 | * and it should be removed from the data structure holding a reference to it. 168 | */ 169 | @CallSuper 170 | protected synchronized final boolean onResult(final @NonNull String permission, Permissions result) { 171 | mPermissions.remove(permission); 172 | boolean onResult = false; 173 | if (result == Permissions.GRANTED) { 174 | getSchedule().scheduleDirect(new Runnable() { 175 | @Override 176 | public void run() { 177 | PermissionsResultAction.this.onGranted(permission); 178 | } 179 | }); 180 | } else if (result == Permissions.DENIED) { 181 | getSchedule().scheduleDirect(new Runnable() { 182 | @Override 183 | public void run() { 184 | PermissionsResultAction.this.onDenied(permission); 185 | } 186 | }); 187 | } else if (result == Permissions.NOT_FOUND) { 188 | if (shouldIgnorePermissionNotFound(permission)) { 189 | getSchedule().scheduleDirect(new Runnable() { 190 | @Override 191 | public void run() { 192 | PermissionsResultAction.this.onGranted(permission); 193 | } 194 | }); 195 | } else { 196 | getSchedule().scheduleDirect(new Runnable() { 197 | @Override 198 | public void run() { 199 | PermissionsResultAction.this.onDenied(permission); 200 | } 201 | }); 202 | } 203 | } else if (result == Permissions.USER_DENIED_FOREVER) { 204 | getSchedule().scheduleDirect(new Runnable() { 205 | @Override 206 | public void run() { 207 | PermissionsResultAction.this.onDeniedForever(permission); 208 | } 209 | }); 210 | } 211 | 212 | //标记不被通过的权限 213 | if (result == Permissions.DENIED || (result == Permissions.NOT_FOUND && !shouldIgnorePermissionNotFound(permission))) { 214 | mDeniedPermissions.add(permission); 215 | } 216 | if (mPermissions.isEmpty()) { 217 | getSchedule().scheduleDirect(new Runnable() { 218 | @Override 219 | public void run() { 220 | if (mDeniedPermissions.isEmpty()) { 221 | PermissionsResultAction.this.onSuccess(); 222 | } else { 223 | final String[] deniedPermissions = (String[]) mDeniedPermissions.toArray( 224 | new String[mDeniedPermissions.size()]); 225 | PermissionsResultAction.this.onFailure(deniedPermissions); 226 | } 227 | //重置不被通过的权限存储 228 | mDeniedPermissions.clear(); 229 | } 230 | }); 231 | onResult = true; 232 | } 233 | return onResult; 234 | } 235 | 236 | /** 237 | * This method registers the PermissionsResultAction object for the specified permissions 238 | * so that it will know which permissions to look for changes to. The PermissionsResultAction 239 | * will then know to look out for changes to these permissions. 240 | * 241 | * @param perms the permissions to listen for 242 | */ 243 | @SuppressWarnings("WeakerAccess") 244 | @CallSuper 245 | protected synchronized final void registerPermissions(@NonNull String[] perms) { 246 | Collections.addAll(mPermissions, perms); 247 | } 248 | 249 | /** 250 | * 获取执行权限回调的线程调度器 251 | * 252 | * @return 253 | */ 254 | protected synchronized Scheduler getSchedule() { 255 | return null == mLooper ? AndroidSchedulers.mainThread() : AndroidSchedulers.from(mLooper); 256 | } 257 | 258 | protected int getRequestCode() { 259 | return mRequestCode; 260 | } 261 | } -------------------------------------------------------------------------------- /simplepermission/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | simplePermission 3 | 4 | -------------------------------------------------------------------------------- /simplepermission_ano/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /simplepermission_ano/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | apply plugin: 'maven' 4 | 5 | //加载资源 6 | Properties properties = new Properties() 7 | InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream(); 8 | properties.load(inputStream) 9 | 10 | publish { 11 | bintrayUser = properties.getProperty("bintrayUser") 12 | bintrayKey = properties.getProperty("bintrayKey") 13 | userOrg = 'ykbjson' // bintray注册的用户名 14 | groupId = rootProject.ext.bintray.group 15 | artifactId = 'simplepermission_ano'// bintray创建的package 16 | publishVersion = rootProject.ext.bintray.anoVersion 17 | desc = 'SimplePermission权限申请库注解' 18 | } 19 | 20 | dependencies { 21 | implementation fileTree(dir: 'libs', include: ['*.jar']) 22 | } 23 | 24 | sourceCompatibility = "1.7" 25 | targetCompatibility = "1.7" 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /simplepermission_ano/src/main/java/com/ykbjson/lib/simplepermission/ano/PermissionNotify.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.lib.simplepermission.ano; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Desription:onRequestPermissionsResult方法回调的接收者 10 | * Creator:yankebin 11 | * CreatedAt:2018/10/29 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.TYPE) 15 | public @interface PermissionNotify { 16 | } 17 | -------------------------------------------------------------------------------- /simplepermission_ano/src/main/java/com/ykbjson/lib/simplepermission/ano/PermissionRequest.java: -------------------------------------------------------------------------------- 1 | package com.ykbjson.lib.simplepermission.ano; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Desription:请求权限的方法注解 10 | * Creator:yankebin 11 | * CreatedAt:2018/10/29 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.METHOD) 15 | public @interface PermissionRequest { 16 | 17 | /** 18 | * 申请权限的code,在权限回调的时候可以根据code知道哪些权限被授予或拒绝 19 | */ 20 | int requestCode() default 0; 21 | 22 | /** 23 | * 要申请的权限数组 24 | */ 25 | String[] requestPermissions() default {""}; 26 | 27 | /** 28 | * 是否需要在申请成功后再次执行代码逻辑 29 | */ 30 | boolean needReCall() default false; 31 | } 32 | --------------------------------------------------------------------------------