├── .gitignore
├── LICENSE.txt
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── holo
│ │ └── android
│ │ └── permission
│ │ └── sample
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── holo
│ │ │ └── android
│ │ │ └── permission
│ │ │ └── sample
│ │ │ ├── MainActivity.kt
│ │ │ └── Usage.kt
│ └── res
│ │ ├── layout
│ │ └── activity_main.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
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── holo
│ └── android
│ └── permission
│ └── sample
│ └── ExampleUnitTest.java
├── build.gradle
├── doc
├── Manuscript.md
├── README_USAGE.md
├── ReleaseNote.md
├── What is "HaloPermission".md
├── app-release.apk
├── rationale_render.gif
└── setting_render.gif
├── permission
├── .gitignore
├── bintray.gradle
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── halo
│ └── android
│ └── permission
│ ├── HaloPermission.kt
│ ├── HaloSpecPermission.kt
│ ├── caller
│ ├── PermissionCaller.kt
│ ├── PermissionFragment.kt
│ └── RuntimePermissionFragmentCaller.kt
│ ├── checker
│ ├── PermissionChecker.kt
│ ├── StandardChecker.kt
│ ├── StrictChecker.kt
│ └── strict
│ │ ├── BaseCheck.kt
│ │ ├── BodySensorCheck.kt
│ │ ├── CameraCheck.kt
│ │ ├── CameraCheck21.kt
│ │ ├── LocationCheck.kt
│ │ ├── NormalCheck.kt
│ │ ├── ReadCalendarCheck.kt
│ │ ├── ReadCallLogCheck.kt
│ │ ├── ReadContactsCheck.kt
│ │ ├── ReadExternalStorageCheck.kt
│ │ ├── ReadPhoneStateCheck.kt
│ │ ├── ReadSMSCheck.kt
│ │ ├── RecordAudioCheck.kt
│ │ ├── WriteCalendarCheck.kt
│ │ ├── WriteCallLogCheck.kt
│ │ ├── WriteContactsCheck.kt
│ │ └── WriteExternalStorageCheck.kt
│ ├── common
│ ├── IntentSupport.kt
│ ├── PLog.kt
│ └── Util.kt
│ ├── processor
│ ├── PermissionProcessor.kt
│ ├── PermissionResponder.kt
│ └── PermissionState.kt
│ ├── request
│ ├── DefaultRender.kt
│ ├── PermissionRequest.kt
│ ├── RationaleRender.kt
│ └── RequestListener.kt
│ ├── setting
│ ├── PermissionSetting.kt
│ ├── SettingRender.kt
│ └── SettingResponder.kt
│ └── spec
│ ├── SpecPermission.kt
│ ├── SpecPermissionFragmentCaller.kt
│ ├── SpecPermissionImpl.kt
│ ├── SpecPermissionNotification.kt
│ ├── SpecType.java
│ ├── SpecialCaller.kt
│ └── SpecialListener.kt
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
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 (C) 2018 Lucio
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 | [](https://github.com/SupLuo/HaloPermission/releases)
2 | [](https://github.com/SupLuo/HaloPermission/blob/master/LICENSE.txt)
3 |
4 | ## HoloPermission
5 | An Android permission library developed by Kotlin language with higher extensibility and compatibility.
6 |
7 | Kotlin语言开发的具有高可扩展性和可控性的Android权限库。
8 |
9 | ### Features
10 | * 良好的设计和可扩展性(个人觉得最区别于别的库的最主要原因)
11 | * 兼容到8.0所需要的相关特殊权限,并可以很好的进行扩展(未知来源、悬浮窗、系统设置修改、通知、通知渠道等权限的处理)
12 | * 支持RationaleRender(向用户解释为什么申请权限)
13 | * 支持SettingRender(当权限没有授权时,可以打开权限设置界面进行设置,并自动检测设置是否改变)
14 | * 更好的系统权限设置界面兼容性(尽量兼容不同的系统厂商的权限设置界面)
15 | * 6.0以下权限支持(不建议,感觉没必要了)
16 |
17 |
18 | ## [HaloPermission解惑(Why choose "HaloPermission")](https://github.com/SupLuo/HaloPermission/blob/master/doc/What%20is%20%22HaloPermission%22.md)
19 | 您可能对HaloPermission有诸多疑问:已经有那么多成熟的权限处理框架,为什么要用这个?这个有什么值得使用的理由?为什么这个库的星星这么少?这库到底靠谱不靠谱?...
20 | 希望[HaloPermission解惑](https://github.com/SupLuo/HaloPermission/blob/master/doc/What%20is%20%22HaloPermission%22.md)能够消除您心中的疑虑。
21 |
22 |
23 | ### Setup
24 |
25 | 在使用HaloPermission的Module的build.gradle中添加以下依赖:
26 | ```
27 | dependencies {
28 | //gradle 3.+以后不是使用'compile'方法,而是使用'implementation' or 'api'等方式
29 | compile('halo.android:permission:1.0.5-rc@aar'){
30 | //传递依赖
31 | transitive = true
32 | }
33 | }
34 |
35 | ```
36 |
37 | 额外配置说明:
38 |
39 | 1. V7依赖说明
40 |
41 | HaloPermission依赖`com.android.support:appcompat-v7:28.0.0`包(本身也依赖v4包,但v7依赖v4,所以引入v7即可),如果与您工程依赖的版本不一致,你可以排除HaloPermission的版本依赖。
42 | 但是`appcompat-v7`的版本不能低于24.1.+
43 |
44 | ```
45 | compile('halo.android:permission:1.0.5-rc@aar'){
46 | //传递依赖
47 | transitive = true
48 | //排除appcompat-v7依赖
49 | exclude group: 'com.android.support', module: 'appcompat-v7'
50 | }
51 | ```
52 |
53 | 2. Kotlin依赖说明(项目不支持Kotlin开发的童鞋可以忽略)
54 |
55 | HaloPermission是基于`org.jetbrains.kotlin:kotlin-stdlib:1.3.11`开发的,如果您的工程也支持Kotlin开发,并且与HaloPermission版本不一致,您可以排除HaloPermission对`kotlin-stdlib`的依赖。
56 | 但是`kotlin-stdlib`的版本最好不能低于1.3.11
57 | ```
58 | compile('halo.android:permission:1.0.5-rc@aar'){
59 | //传递依赖
60 | transitive = true
61 | //排除kotlin-stdlib依赖
62 | exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
63 | }
64 | ```
65 |
66 | ### Usage
67 | **[查看详细使用](https://github.com/SupLuo/HoloPermission/blob/master/doc/README_USAGE.md)**
68 |
69 | ### 运行效果图
70 | 直接下载demo运行吧。。。
71 | [Demo下载地址](https://github.com/SupLuo/HaloPermission/blob/master/doc/app-release.apk)
72 |
73 | ### 缺陷说明
74 | 对于6.0以下的权限处理,某些权限校验**可能**并不准确,这一部分稍后进一步说明原因。
75 | 不仅HaloPermission如此,大多数有名的框架也不能解决这些问题。但是这并不影响app的正常逻辑流程,因为在后续流程中你能够得到更具体的一些信息。
76 |
77 | ### 联系方式
78 | QQ:862638161
79 |
80 | Email:super_luo@163.com
81 |
82 | 如需交流,欢迎讨论
83 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "holo.android.permission.sample"
7 | minSdkVersion 15
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(include: ['*.jar'], dir: 'libs')
23 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
24 | transitive = true
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
27 | })
28 | implementation "com.android.support:appcompat-v7:$support_version"
29 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
30 | implementation 'com.android.support.constraint:constraint-layout:1.0.2'
31 | testCompile 'junit:junit:4.12'
32 |
33 | implementation 'com.qw:soulpermission:1.1.8'
34 | implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
35 |
36 | // implementation 'halo.android:permission:1.0.2-rc@aar'
37 | implementation project(':permission')
38 | }
39 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/Lucio/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/holo/android/permission/sample/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package holo.android.permission.sample;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("holo.android.permission.sample", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/holo/android/permission/sample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package holo.android.permission.sample
2 |
3 | import android.Manifest
4 | import android.annotation.TargetApi
5 | import android.app.AlertDialog
6 | import android.app.NotificationChannel
7 | import android.app.NotificationManager
8 | import android.content.Context
9 | import android.content.Intent
10 | import android.net.Uri
11 | import android.os.Build
12 | import android.os.Bundle
13 | import android.support.v4.app.NotificationCompat
14 | import android.support.v7.app.AppCompatActivity
15 | import android.view.View
16 | import android.view.WindowManager
17 | import android.widget.TextView
18 | import android.widget.Toast
19 | import halo.android.permission.HaloPermission
20 | import halo.android.permission.HaloSpecPermission
21 | import halo.android.permission.common.PLog
22 | import halo.android.permission.request.DefaultRationaleRender
23 | import halo.android.permission.request.DefaultSettingRender
24 | import halo.android.permission.request.PermissionListener
25 | import halo.android.permission.request.RationaleRender
26 | import halo.android.permission.setting.SettingRender
27 | import halo.android.permission.spec.SpecialListener
28 | import java.lang.RuntimeException
29 |
30 |
31 | class MainActivity : AppCompatActivity() {
32 |
33 | private val NOTIFICATION_CHANEL = "default"
34 |
35 | override fun onCreate(savedInstanceState: Bundle?) {
36 | super.onCreate(savedInstanceState)
37 | setContentView(R.layout.activity_main)
38 | //
39 | // var fis: FileInputStream? = null
40 | // try {
41 | // val buildProperties = Properties()
42 | // fis = FileInputStream(File(Environment.getRootDirectory(), "build.prop"))
43 | // buildProperties.load(fis)
44 | // val entries = buildProperties.entries
45 | //
46 | // for (item in entries){
47 | // println("${item.key} = ${item.value}")
48 | // }
49 | // } catch (e: Exception) {
50 | // e.printStackTrace()
51 | // } finally {
52 | // try {
53 | // fis?.close()
54 | // } catch (e: Exception) {
55 | // }
56 | // }
57 | }
58 |
59 | fun callPhone() {
60 | val it = Intent(Intent.ACTION_CALL, Uri.parse("tel:10086"))
61 | startActivity(it)
62 | }
63 |
64 | fun btn1Click(v: View?) {
65 | HaloPermission.newRequest( Manifest.permission.CALL_PHONE)
66 | .setListener(object : PermissionListener {
67 | override fun onPermissionDenied(permissions: List) {
68 | toast("不允许打电话")
69 |
70 | }
71 |
72 | override fun onPermissionGrand(permissions: List) {
73 | toast("允许打电话")
74 | callPhone()
75 | }
76 |
77 | }).request(this)
78 | }
79 |
80 |
81 | fun btn2Click(v: View?) {
82 | HaloPermission.newRequest( Manifest.permission.WRITE_EXTERNAL_STORAGE)
83 | .setRationaleRender(DefaultRationaleRender("为保障功能的正常使用,请允许程序访问外部存储。",
84 | "温馨提示", "确定", "取消"))
85 | .setListener(object : PermissionListener {
86 | override fun onPermissionDenied(permissions: List) {
87 | toast("不允许访问外部存储")
88 |
89 | }
90 |
91 | override fun onPermissionGrand(permissions: List) {
92 | toast("允许访问外部存储")
93 | callPhone()
94 | }
95 |
96 | }).request(this)
97 | }
98 |
99 | fun btn3Click(v: View?) {
100 | HaloPermission.newRequest(Manifest.permission.CAMERA)
101 | .setSettingRender(DefaultSettingRender("无法调用相机,请允许权限。",
102 | "温馨提示", "设置", "取消"))
103 | .setListener(object : PermissionListener {
104 | override fun onPermissionDenied(permissions: List) {
105 | toast("不允许使用相机")
106 |
107 | }
108 |
109 | override fun onPermissionGrand(permissions: List) {
110 | toast("允许使用相机")
111 | callPhone()
112 | }
113 |
114 | }).request(this)
115 | }
116 |
117 | fun btn4Click(v: View?) {
118 | HaloPermission.newRequest(Manifest.permission.READ_CONTACTS,
119 | Manifest.permission.SEND_SMS)
120 | .setRationaleRender(object:RationaleRender{
121 | override fun show(ctx: Context, permission: List, process: RationaleRender.Process) {
122 | AlertDialog.Builder(ctx)
123 | .setMessage("这是一个权限申请解释:为了更好的使用功能,请允许接下来的权限申请")
124 | .setPositiveButton("确认") { dialog, which ->
125 | process.onNext()
126 | }
127 | .setNegativeButton("取消") { dialog, which ->
128 | process.onCancel()
129 | }
130 | .setOnCancelListener {
131 | process.onCancel()
132 | }
133 | .create().show()
134 | }
135 |
136 | })
137 | .setSettingRender(object:SettingRender{
138 | override fun show(ctx: Context, permission: List, process: SettingRender.Process) {
139 | var alertMsg = ""
140 | if(permission.contains(Manifest.permission.READ_CONTACTS) ){
141 | if(permission.contains(Manifest.permission.SEND_SMS)){
142 | alertMsg ="无法读取联系人和发送短信息,请允许权限"
143 | }else{
144 | alertMsg ="无法读取联系人,请允许权限"
145 | }
146 | }else if(permission.contains(Manifest.permission.SEND_SMS)){
147 | alertMsg ="无法发送短信息,请允许权限"
148 | }
149 | AlertDialog.Builder(ctx)
150 | .setMessage(alertMsg)
151 | .setPositiveButton("设置") { dialog, which ->
152 | process.onNext(true)
153 | }
154 | .setNegativeButton("取消") { dialog, which ->
155 | process.onCancel()
156 | }
157 | .setOnCancelListener {
158 | process.onCancel()
159 | }
160 | .create().show()
161 | }
162 | })
163 |
164 | .setListener(object : PermissionListener {
165 | override fun onPermissionDenied(permissions: List) {
166 | toast("权限申请失败${permissions.joinToString(",")}")
167 | }
168 |
169 | override fun onPermissionGrand(permissions: List) {
170 | toast("权限申请成功")
171 | }
172 |
173 | }).request(this)
174 | }
175 |
176 | private fun tryUi(action: () -> Unit) {
177 | try {
178 | action()
179 | } catch (e: Exception) {
180 | toast(e.message.orEmpty())
181 | }
182 | }
183 |
184 | fun toast(msg: String) {
185 | Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
186 | }
187 |
188 | fun btnUnkonwnSourceAppClick(v: View) {
189 | try {
190 | HaloSpecPermission.requestAppUnknownSource(this, object : SpecialListener {
191 | override fun onSpecialGrand() {
192 | toast("允许未知来源")
193 | }
194 |
195 | override fun onSpecialDeny() {
196 | toast("禁止未知来源")
197 | }
198 |
199 | override fun showRationalView(process: SpecialListener.Process) {
200 | AlertDialog.Builder(this@MainActivity)
201 | .setMessage("无法安装程序,为了能够正常的安装程序,请允许接下来请求的设置")
202 | .setPositiveButton("设置") { dialog, which ->
203 | process.onSpecialNext()
204 | }
205 | .setNegativeButton("取消") { dialog, which ->
206 | process.onSpecialCancel()
207 | }
208 | .setOnCancelListener {
209 | process.onSpecialCancel()
210 | }
211 | .create().show()
212 | }
213 | })
214 | } catch (e: Exception) {
215 | PLog.d("btnUnkonwnSourceAppClick:${e.message}")
216 | }
217 | }
218 |
219 | fun btnNotificationClick(v: View) {
220 | tryUi {
221 | HaloSpecPermission.requestNotification(this, object : SpecialListener {
222 | override fun onSpecialGrand() {
223 | toast("允许通知")
224 | showNotification()
225 | }
226 |
227 | override fun onSpecialDeny() {
228 | toast("禁止通知")
229 | }
230 | })
231 | }
232 |
233 | }
234 |
235 | fun btnNotificationChannelClick(v: View) {
236 | tryUi {
237 | HaloSpecPermission.requestNotificationChanel(this, NOTIFICATION_CHANEL, object : SpecialListener {
238 | override fun onSpecialGrand() {
239 | toast("允许通知渠道${NOTIFICATION_CHANEL}")
240 | }
241 |
242 | override fun onSpecialDeny() {
243 | toast("禁止通知渠道${NOTIFICATION_CHANEL}")
244 | }
245 |
246 | })
247 | }
248 | }
249 |
250 | @TargetApi(Build.VERSION_CODES.O)
251 | private fun showNotification() {
252 |
253 | tryUi {
254 | val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
255 |
256 |
257 | var channelEnabled = HaloSpecPermission.areNotificationChannelsEnabled(this, NOTIFICATION_CHANEL)
258 | if(!channelEnabled){
259 | val channel = NotificationChannel(
260 | NOTIFICATION_CHANEL,
261 | "你有新的消息",
262 | NotificationManager.IMPORTANCE_DEFAULT
263 |
264 | )
265 | notificationManager.createNotificationChannel(channel)
266 | }
267 |
268 | channelEnabled = HaloSpecPermission.areNotificationChannelsEnabled(this, NOTIFICATION_CHANEL)
269 |
270 | if(!channelEnabled)
271 | throw RuntimeException("当前通知渠道不可用,无法进行通知,可以点击通知渠道按钮申请")
272 |
273 | val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANEL)
274 | .setSmallIcon(R.mipmap.ic_launcher_round)
275 | .setContentTitle("title")
276 | .setContentText("content")
277 | .build()
278 | notificationManager.notify(1, notification)
279 | }
280 |
281 | }
282 |
283 | fun btnDrawOverlaysSettingClick(v: View) {
284 | HaloSpecPermission.requestDrawOverlays(this, object : SpecialListener {
285 | override fun onSpecialGrand() {
286 | toast("允许悬浮窗")
287 | addOverlays()
288 | }
289 |
290 | override fun onSpecialDeny() {
291 | toast("禁止悬浮窗")
292 | }
293 | })
294 | }
295 |
296 | private fun addOverlays() {
297 | val view = TextView(this)
298 | .apply {
299 | text = "这是一个悬浮窗"
300 | setBackgroundResource(android.R.color.black)
301 | setTextColor(resources.getColor(android.R.color.white))
302 | setOnClickListener {
303 | windowManager.removeView(this)
304 | }
305 | }
306 | val lp = WindowManager.LayoutParams()
307 | .apply {
308 | width = WindowManager.LayoutParams.WRAP_CONTENT
309 | height = WindowManager.LayoutParams.WRAP_CONTENT
310 | type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
311 | flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
312 | }
313 | windowManager.addView(view, lp)
314 | }
315 |
316 | fun btnWriteSystemSettingClick(v: View) {
317 | HaloSpecPermission.requestWriteSystemSetting(this, object : SpecialListener {
318 | override fun onSpecialGrand() {
319 | toast("允许修改系统设置")
320 | }
321 |
322 | override fun onSpecialDeny() {
323 | toast("禁止修改系统设置")
324 | }
325 | })
326 | }
327 |
328 | }
--------------------------------------------------------------------------------
/app/src/main/java/holo/android/permission/sample/Usage.kt:
--------------------------------------------------------------------------------
1 | //package holo.android.permission.sample
2 | //
3 | //import android.Manifest
4 | //import android.app.Activity
5 | //import android.content.Context
6 | //import android.content.Intent
7 | //import android.widget.Toast
8 | //import halo.android.permission.HoloPermission
9 | //import halo.android.permission.caller.PermissionCaller
10 | //import halo.android.permission.caller.PermissionResponder
11 | //import halo.android.permission.checker.PermissionChecker
12 | //import halo.android.permission.invokeRequest.GrandAction
13 | //import halo.android.permission.invokeRequest.PermissionListener
14 | //import halo.android.permission.invokeRequest.RationaleRender
15 | //import halo.android.permission.invokeRequest.SettingRender
16 | //
17 | ///**
18 | // * Created by Lucio on 18/4/10.
19 | // */
20 | //
21 | //class Usage : Activity() {
22 | // fun invokeRequest() {
23 | // HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CALL_PHONE)
24 | // .setListener(object: PermissionListener{
25 | // override fun onPermissionDenied(permissions: List) {
26 | //
27 | // }
28 | // override fun onPermissionGrand(permissions: List) {
29 | // }
30 | // }).run()
31 | //
32 | // val permissions:Array = arrayOf("","")
33 | // HoloPermission.with(this,*permissions)
34 | // }
35 | //
36 | // fun request2() {
37 | // HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
38 | // .setGrandAction(object:GrandAction{
39 | // override fun onPermissionGrand(permissions: List) {
40 | //
41 | // }
42 | //
43 | // }).run()
44 | // }
45 | //
46 | // fun request3() {
47 | // HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
48 | // .setListener(object: PermissionListener{
49 | // override fun onPermissionDenied(permissions: List) {
50 | //
51 | // }
52 | // override fun onPermissionGrand(permissions: List) {
53 | // }
54 | // })
55 | // .setRationaleRender("为了确保功能的正常使用,请允许接下来的权限请求申请。")
56 | // .run()
57 | // }
58 | //
59 | // fun request4() {
60 | // HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
61 | // .setListener(object: PermissionListener{
62 | // override fun onPermissionDenied(permissions: List) {
63 | //
64 | // }
65 | // override fun onPermissionGrand(permissions: List) {
66 | // }
67 | // })
68 | // .setRationaleRender(object:RationaleRender{
69 | // override fun show(ctx: Context, permission: List, process: RationaleRender.Process) {
70 | // Toast.makeText(ctx,"为了确保功能的正常使用,请允许接下来的权限请求申请。",Toast.LENGTH_SHORT).show()
71 | // process.onNext()
72 | // }
73 | // })
74 | // .run()
75 | // }
76 | //
77 | // fun request5() {
78 | // HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
79 | // .setListener(object: PermissionListener{
80 | // override fun onPermissionDenied(permissions: List) {
81 | //
82 | // }
83 | // override fun onPermissionGrand(permissions: List) {
84 | // }
85 | // })
86 | // .setSettingRender("无法使用外部存储,请设置权限以便使用。")
87 | // .run()
88 | // }
89 | //
90 | // fun request6() {
91 | // HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
92 | // .setListener(object: PermissionListener{
93 | // override fun onPermissionDenied(permissions: List) {
94 | //
95 | // }
96 | // override fun onPermissionGrand(permissions: List) {
97 | // }
98 | // })
99 | // .setSettingRender(object:SettingRender{
100 | // override fun getCustomSettingIntent(ctx: Context): Intent? {
101 | // return super.getCustomSettingIntent(ctx)
102 | // }
103 | //
104 | // override fun show(ctx: Context, permission: List, process: SettingRender.Process) {
105 | // Toast.makeText(ctx,"为了确保功能的正常使用,请允许接下来的权限请求申请。",Toast.LENGTH_SHORT).show()
106 | // process.onNext()
107 | // }
108 | // })
109 | // .run()
110 | // }
111 | //
112 | // class CustomChecker:PermissionChecker{
113 | // override fun isPermissionGranted(ctx: Context, permission: String): Boolean {
114 | // {}
115 | // }
116 | // }
117 | //
118 | //
119 | //}
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
31 |
32 |
41 |
42 |
51 |
52 |
61 |
62 |
71 |
72 |
73 |
82 |
83 |
92 |
93 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/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/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | HaloPermission
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/holo/android/permission/sample/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package holo.android.permission.sample;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Stack;
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() throws Exception {
15 | // cal("(1+((2*3)-(5+4)))");
16 | //
17 | // cal("1+2*3-(5+4)");
18 | cal("1+2*3-5+4-6+8");
19 | }
20 |
21 | private void cal(String temp) {
22 | Stack ops = new Stack<>();
23 | Stack value = new Stack<>();
24 |
25 | // Stack[] ff = new Stack[4];
26 |
27 | for (int i = 0; i < temp.length(); i++) {
28 | char item = temp.charAt(i);
29 |
30 | if (item == '(') {
31 |
32 | } else if (item == '+' || item == '-' || item == '*' || item == '/') {
33 | if ((item == '+' || item == '-') && !ops.isEmpty() && value.size() > 1) {
34 | double v1 = value.pop();
35 | String op1 = ops.pop();
36 | double v2 = value.pop();
37 | System.out.println("计算" + 2 + op1 + v1);
38 | //反括号求职
39 | if (op1.equals("+")) {
40 | value.push(v2 + v1);
41 | } else if (op1.equals("-")) {
42 | value.push(v2 - v1);
43 | } else if (op1.equals("*")) {
44 | value.push(v2 * v1);
45 | } else if (op1.equals("/")) {
46 | value.push(v2 / v1);
47 | }
48 |
49 | }
50 |
51 | ops.push(String.valueOf(item));
52 | } else if (item == ')') {
53 | double v1 = value.pop();
54 | String op1 = ops.pop();
55 | double v2 = value.pop();
56 | System.out.println("计算" + v2 + op1 + v1);
57 |
58 | try {
59 | //反括号求职
60 | if (op1.equals("+")) {
61 | value.push(v2 + v1);
62 | } else if (op1.equals("-")) {
63 | value.push(v2 - v1);
64 | } else if (op1.equals("*")) {
65 | value.push(v2 * v1);
66 | } else if (op1.equals("/")) {
67 | value.push(v2 / v1);
68 | }
69 |
70 | } catch (Exception e) {
71 | e.printStackTrace();
72 | }
73 | } else {
74 | value.push(Double.parseDouble(String.valueOf(item)));
75 | }
76 | }
77 |
78 | while (!ops.isEmpty()) {
79 | double v1 = value.pop();
80 | String op1 = ops.pop();
81 | double v2 = value.pop();
82 | System.out.println("计算" + v2 + op1 + v1);
83 | try {
84 | //反括号求职
85 | if (op1.equals("+")) {
86 | value.push(v2 + v1);
87 | } else if (op1.equals("-")) {
88 | value.push(v2 - v1);
89 | } else if (op1.equals("*")) {
90 | value.push(v2 * v1);
91 | } else if (op1.equals("/")) {
92 | value.push(v2 / v1);
93 | }
94 |
95 | } catch (Exception e) {
96 | e.printStackTrace();
97 | }
98 | }
99 | System.out.println("表达式" + temp + "=" + value.pop());
100 | }
101 |
102 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.11'
5 | ext.support_version = '28.0.0'
6 | repositories {
7 | jcenter()
8 | mavenCentral()
9 | maven {
10 | url 'https://maven.google.com/'
11 | name 'Google'
12 | }
13 | }
14 | dependencies {
15 | classpath 'com.android.tools.build:gradle:3.3.0'
16 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
17 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
18 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
19 | // NOTE: Do not place your application dependencies here; they belong
20 | // in the individual module build.gradle files
21 | }
22 | }
23 |
24 | allprojects {
25 | repositories {
26 | jcenter()
27 | mavenCentral()
28 | maven { url 'https://jitpack.io' }
29 | maven {
30 | url 'https://maven.google.com/'
31 | name 'Google'
32 | }
33 | }
34 |
35 | //加上这些
36 | tasks.withType(Javadoc) {
37 | options.addStringOption('Xdoclint:none', '-quiet')
38 | options.addStringOption('encoding', 'UTF-8')
39 | options.addStringOption('charSet', 'UTF-8')
40 | }
41 | }
42 |
43 | //tasks.getByPath(":permission:mavenAndroidJavadocs").enabled = false
44 | //tasks.getByPath(":permission:releaseAndroidJavadocs").enabled = false
45 |
46 | task clean(type: Delete) {
47 | delete rootProject.buildDir
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/doc/Manuscript.md:
--------------------------------------------------------------------------------
1 | >An Android permission library developed by Kotlin language with higher extensibility and compatibility.
2 |
3 | >Kotlin语言开发的Android权限库,具有更高的扩展性和兼容性。
4 |
5 | ##### 写在前面的
6 | Android运行时权限,想必对Android开发者来说并不陌生,Github上也有不少相应的库也足够应付现在的使用了,但是`HaloPermission`不是在无聊的造轮子,它的职责是让自己提供的支持更完美,更能够拥抱变化。
7 | 其实`Halo`是一个系列,里面的每一个库我都会用心,尽自己所能的去写好,我也希望大家能给予更多的支持,共同建设,让Android开发闪射自己的`Halo`.
8 | 在开发`HaloPermission`之前,我阅读了很多文章,也看过很多库的源码,所以感谢这些伟大的无私奉献者和开源库作者,其中包括`RxPermission`,`HiPermission`,`EasyPermission`,`AndPermission`等。
9 | ##### 为什么是HaloPermission
10 | - 作者的出发点(一个对事情要求完美的处女座特点)
11 | - 基于Kotlin(双刃剑,仁者见仁,智者见智)
12 | - 更多的扩展性(后面会写文章专门介绍HoloPermission的设计)
13 | - 更多的兼容性(尽量兼容);
14 | - 更灵活的功能配置
15 |
16 |
17 | ##### 使用介绍
18 | ###### 1. 常规使用
19 |
20 | * 请求一个权限,然后接收结果回调
21 |
22 |
23 | ```
24 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
25 | .setListener(object: PermissionListener{
26 | override fun onPermissionDenied(permissions: List) {
27 | {your code for deny}
28 | }
29 | override fun onPermissionGrand(permissions: List) {
30 | {your code for grand}
31 | }
32 | }).run()
33 | ```
34 |
35 | * 请求多个权限
36 |
37 | ```
38 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CALL_PHONE)
39 | .{省略代码}
40 |
41 | //or
42 |
43 | val permissions:Array = arrayOf("","")
44 | HoloPermission.with(this,*permissions)
45 | .{省略代码}
46 | ```
47 | * 只关心权限被允许(未被允许)的回调
48 | ```
49 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
50 | .setGrandAction(object:GrandAction{
51 | override fun onPermissionGrand(permissions: List) {
52 | {your code for grand}
53 | }
54 |
55 | }).run()
56 | ```
57 |
58 |
59 |
60 |
61 | ###### 2. RationaleRender使用
62 | 如果你想向用户解释请求权限的原因,你可以使用`setRationaleRender`方法
63 |
64 | ```
65 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
66 | .{省略回调设置代码}
67 | .setRationaleRender("为了确保功能的正常使用,请允许接下来的权限请求申请。")
68 | .run()
69 | ```
70 |
71 | 如果你想自定义RationaleRender的样式,比如:
72 |
73 | ```
74 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
75 | .{省略回调设置代码}
76 | .setRationaleRender(object:RationaleRender{
77 | override fun show(ctx: Context, permission: List, process: RationaleRender.Process) {
78 | //自定义使用了一个`Toast`展示信息。
79 | Toast.makeText(ctx,"为了确保功能的正常使用,请允许接下来的权限请求申请。",Toast.LENGTH_SHORT).show()
80 |
81 | //**为了确保后续的流程继续执行,你需要在适当的时候调用process的`onNext`或`onCancel`方法**
82 | process.onNext()
83 |
84 | //onNext()表示继续后面的执行
85 | //onCancel会取消流程的执行,并且会最终回调onPermissionDenied方法
86 | }
87 | })
88 | .run()
89 | ```
90 |
91 | ###### 3. SettingRender使用
92 |
93 | 对于无法获取权限时,如果你想引导用户打开权限设置界面,你可以使用`setSettingRender`方法
94 |
95 | ```
96 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
97 | .{省略回调设置代码}
98 | .setSettingRender("无法使用外部存储,请设置权限以便使用。")
99 | .run()
100 | ```
101 |
102 | 如果你想自定义SettingRender的样式,比如:
103 |
104 | ```
105 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
106 | .{省略回调设置代码}
107 | .setSettingRender(object:SettingRender{
108 | override fun show(ctx: Context, permission: List, process: SettingRender.Process) {
109 | //自定义使用了一个`Toast`展示信息。
110 | Toast.makeText(ctx,"无法使用外部存储,请设置权限以便使用。",Toast.LENGTH_SHORT).show()
111 |
112 | //**为了确保后续的流程继续执行,你需要在适当的时候调用process的`onNext`或`onCancel`方法**
113 | process.onNext()
114 |
115 | //onNext()表示继续后面的执行,HaloPermission将打开系统应用权限设置界面
116 | //onCancel会取消流程的执行,不会打开系统应用权限设置界面,最终会回调onPermissionDenied方法
117 | }
118 | })
119 | .run()
120 | ```
121 |
122 | 如果你觉得`HaloPermission`打开的权限设置界面不是您所满意的,你可以重写`SettingRender`的`getCustomSettingIntent`方法提供一个`Intent`,如果返回null则将使用HaloPermission的默认方式打开:
123 |
124 | ```
125 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
126 | .{省略回调设置代码}
127 | .setSettingRender(object:SettingRender{
128 | override fun show(ctx: Context, permission: List, process: SettingRender.Process) {
129 | {省略的代码}
130 | }
131 |
132 | //自定义SettingIntent
133 | override fun getCustomSettingIntent(ctx: Context): Intent? {
134 | return super.getCustomSettingIntent(ctx)
135 | }
136 | })
137 | .run()
138 | ```
139 |
140 | ###### 4. 自定义权限校验规则
141 |
142 | 两步即可实现
143 |
144 | ```
145 | //1. 创建自定义PermissionChecker
146 | class CustomChecker:PermissionChecker{
147 | override fun isPermissionGranted(ctx: Context, permission: String): Boolean {
148 | {使用你的规则}
149 | }
150 | }
151 |
152 | //2. 使用自定义规则
153 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
154 | .{省略常规代码}
155 | .run(CustomChecker())
156 |
157 | ```
158 |
159 | **除非你非常有把握,否则不建议使用自定义权限校验规则,因为HaloPermission会尽可能的去适配和兼容**
160 |
161 | ###### 5. 自定义请求方式
162 |
163 | HaloPermission默认使用ShadowActivity的形式请求权限,当然只要你愿意,您可以使用Fragment的形式去实现,HaloPermission本身也提供了
164 | Fragment的请求方式,但是最终去掉了这部分的实现,因为对于Fragment的使用机制,如果使用不当,可能会出现一些奇怪的问题,我想这是你我都不愿看到的。
165 |
166 | 同样的,两步即可实现自定义请求方式
167 |
168 | ```
169 | //1. 创建自定义PermissionCaller
170 | class CustomCaller: PermissionCaller{
171 | override fun requestPermission(ctx: Context, responder: PermissionResponder, vararg permision: String) {
172 | {可以仿造HaloPermission实现,最终要在适当的时候调用responder让流程正常进行}
173 | }
174 | }
175 |
176 | //2. 使用自定义规则
177 | HoloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
178 | .{省略常规代码}
179 | .run(CustomCaller())
180 |
181 | ```
182 |
183 | ##### 实际运行效果截图
184 | 熟话说无图无真相,由于常规请求的效果图比较单调,这里只贴了设置了RationaleRender和SettingRender的效果截图:
185 | * 包含SettingRender的效果
186 |
187 | 
188 |
189 | * 包含RationaleRender的效果
190 |
191 | 
192 |
193 |
194 |
195 |
196 | #更多请见 [Github](https://github.com/SupLuo/HaloPermission)
197 |
198 |
199 |
--------------------------------------------------------------------------------
/doc/README_USAGE.md:
--------------------------------------------------------------------------------
1 | #### 1. 常规使用
2 |
3 | * 请求一个权限,然后接收结果回调
4 | ```
5 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
6 | .setListener(object: PermissionListener{
7 | override fun onPermissionDenied(permissions: List) {
8 | {your code for deny}
9 | }
10 | override fun onPermissionGrand(permissions: List) {
11 | {your code for grand}
12 | }
13 | }).run()
14 | ```
15 |
16 | * 请求多个权限
17 |
18 | ```
19 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CALL_PHONE)
20 | .{省略代码}
21 |
22 | //or
23 |
24 | val permissions:Array = arrayOf("","")
25 | HaloPermission.with(this,*permissions)
26 | .{省略代码}
27 | ```
28 | * 只关心权限被允许(未被允许)的回调
29 | ```
30 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
31 | .setGrandAction(object:GrandAction{
32 | override fun onPermissionGrand(permissions: List) {
33 | {your code for grand}
34 | }
35 |
36 | }).run()
37 | ```
38 |
39 |
40 |
41 |
42 | #### 2. RationaleRender使用
43 | 如果你想向用户解释请求权限的原因,你可以使用`setRationaleRender`方法
44 |
45 | ```
46 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
47 | .{省略回调设置代码}
48 | .setRationaleRender("为了确保功能的正常使用,请允许接下来的权限请求申请。")
49 | .run()
50 | ```
51 |
52 | 如果你想自定义RationaleRender的样式,比如:
53 |
54 | ```
55 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
56 | .{省略回调设置代码}
57 | .setRationaleRender(object:RationaleRender{
58 | override fun show(ctx: Context, permission: List, process: RationaleRender.Process) {
59 | //自定义使用了一个`Toast`展示信息。
60 | Toast.makeText(ctx,"为了确保功能的正常使用,请允许接下来的权限请求申请。",Toast.LENGTH_SHORT).show()
61 |
62 | //**为了确保后续的流程继续执行,你需要在适当的时候调用process的`onNext`或`onCancel`方法**
63 | process.onNext()
64 |
65 | //onNext()表示继续后面的执行
66 | //onCancel会取消流程的执行,并且会最终回调onPermissionDenied方法
67 | }
68 | })
69 | .run()
70 | ```
71 |
72 | #### 3. SettingRender使用
73 |
74 | 对于无法获取权限时,如果你想引导用户打开权限设置界面,你可以使用`setSettingRender`方法
75 |
76 | ```
77 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
78 | .{省略回调设置代码}
79 | .setSettingRender("无法使用外部存储,请设置权限以便使用。")
80 | .run()
81 | ```
82 |
83 | 如果你想自定义SettingRender的样式,比如:
84 |
85 | ```
86 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
87 | .{省略回调设置代码}
88 | .setSettingRender(object:SettingRender{
89 | override fun show(ctx: Context, permission: List, process: SettingRender.Process) {
90 | //自定义使用了一个`Toast`展示信息。
91 | Toast.makeText(ctx,"无法使用外部存储,请设置权限以便使用。",Toast.LENGTH_SHORT).show()
92 |
93 | //**为了确保后续的流程继续执行,你需要在适当的时候调用process的`onNext`或`onCancel`方法**
94 | process.onNext()
95 |
96 | //onNext()表示继续后面的执行,HaloPermission将打开系统应用权限设置界面
97 | //onCancel会取消流程的执行,不会打开系统应用权限设置界面,最终会回调onPermissionDenied方法
98 | }
99 | })
100 | .run()
101 | ```
102 |
103 | 如果你觉得`HaloPermission`打开的权限设置界面不是您所满意的,你可以重写`SettingRender`的`getCustomSettingIntent`方法提供一个`Intent`,如果返回null则将使用HaloPermission的默认方式打开:
104 |
105 | ```
106 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
107 | .{省略回调设置代码}
108 | .setSettingRender(object:SettingRender{
109 | override fun show(ctx: Context, permission: List, process: SettingRender.Process) {
110 | {省略的代码}
111 | }
112 |
113 | //自定义SettingIntent
114 | override fun getCustomSettingIntent(ctx: Context): Intent? {
115 | return super.getCustomSettingIntent(ctx)
116 | }
117 | })
118 | .run()
119 | ```
120 |
121 | #### 4. 自定义权限校验规则
122 |
123 | 两步即可实现
124 |
125 | ```
126 | //1. 创建自定义PermissionChecker
127 | class CustomChecker:PermissionChecker{
128 | override fun isPermissionGranted(ctx: Context, permission: String): Boolean {
129 | {使用你的规则}
130 | }
131 | }
132 |
133 | //2. 使用自定义规则
134 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
135 | .{省略常规代码}
136 | .run(CustomChecker())
137 |
138 | ```
139 |
140 | **除非你非常有把握,否则不建议使用自定义权限校验规则,因为HaloPermission会尽可能的去适配和兼容**
141 |
142 | #### 5. 自定义请求方式
143 |
144 | HaloPermission默认使用ShadowActivity的形式请求权限,当然只要你愿意,您可以使用Fragment的形式去实现,HaloPermission本身也提供了
145 | Fragment的请求方式,但是最终去掉了这部分的实现,因为对于Fragment的使用机制,如果使用不当,可能会出现一些奇怪的问题,我想这是你我都不愿看到的。
146 |
147 | 同样的,两步即可实现自定义请求方式
148 |
149 | ```
150 | //1. 创建自定义PermissionCaller
151 | class CustomCaller: PermissionCaller{
152 | override fun requestPermission(ctx: Context, responder: PermissionResponder, vararg permision: String) {
153 | {可以仿造HaloPermission实现,最终要在适当的时候调用responder让流程正常进行}
154 | }
155 | }
156 |
157 | //2. 使用自定义规则
158 | HaloPermission.with(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
159 | .{省略常规代码}
160 | .run(CustomCaller())
161 |
162 | ```
163 |
164 |
165 | #### 6. 使用原始的上下文请求权限
166 |
167 | ```
168 | 1.在请求权限时使用original方法替代with方法
169 | HaloPermission.original(this,Manifest.permission.READ_CONTACTS)
170 | ....
171 |
172 | 2.在当前上下文中(Activity/Fragment)重写onRequestPermissionsResult方法
173 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
174 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
175 | HaloPermission.onRequestPermissionsResult(requestCode, permissions, grantResults)
176 | }
177 |
178 | ```
--------------------------------------------------------------------------------
/doc/ReleaseNote.md:
--------------------------------------------------------------------------------
1 | ## 0.9-rc
2 | * 支持SettingRender
3 | * 支持RationaleRender
4 | * 提供标准权限检查Checker实现
5 | * 提供Activity权限请求方式Caller实现
6 |
7 | ## 1.0-rc
8 | * 提供严格权限检查实现(通过对权限的具体使用判断权限是否可用)
9 | * 兼容6.0以下权限
10 |
11 | ## 1.0.1-rc
12 | * 以更合理的方式提供依赖传递
13 |
14 | ## 1.0.2-rc
15 | * 支持使用原始的上下文(用户当前界面)请求权限
--------------------------------------------------------------------------------
/doc/What is "HaloPermission".md:
--------------------------------------------------------------------------------
1 | ## 关于权限,你所需要了解的
2 | 对于权限的常规介绍以及使用这种教科书式的内容,已经烂大街了,所以需要补充这方面认识的可以查看官网,可以google、百度,而我想聊的是一些权限"不正常的东西"
3 | 。
4 |
5 | 对于最复杂的权限请求莫过于:先判断某个权限是否被授权,如果未被授权,根据业务规则决定是否要在请求权限之前向用户解释请求权限的原因,之后发起权限请求,如果请求之后权限结果还是未被允许,则根据业务逻辑规则决定是否需要引导用户进入系统权限设置界面,对于进入系统权限设置界面之后返回,可能还想知道用户的设置结果。
6 | 对于其它流程应该都是比这个流程更简单的,对于SDK提供的API本身也够用了,也足够应付这些需求了。但在设备提供商/系统提供商如此多样化的国内,所有的请求流程可能并没有想象的那么顺畅。
7 |
8 | 在国内,你可能遇到如下问题:
9 |
10 | 1. 权限校验结果与实际不相符
11 | 明明没有权限,但是通过API请求得到的权限结果是授权了的。
12 | 2. `shouldShowRequestPermissionRationale`总是返回false或true
13 | 是否需要向用户解释请求权限的原因通常是通过这个方法的返回值来进一步确定的。如果这一步不准确,可能永远都无法向用户解释原因。
14 | 3. 无法打开想要的系统权限设置界面
15 | 国内很多设备系统由于被高度定制,各大厂商搞各自特性,导致打开权限系统设置界面并没有统一方法。
16 | 4. 用户关闭系统权限设置界面之后,没有自动刷新权限请求结果
17 | app无法得到权限,引导用户打开了权限设置界面,用户关闭权限设置界面之后,app无法得到用户操作权限的结果从而进一步处理业务逻辑。
18 | 5. 无法支持6.0之前的权限处理
19 | android运行时权限是在6.0之后才提供的,对于以往版本的权限处理,默认返回的是true,但是用户可能通过设置界面或者手机管理软件禁止app的权限,而app在运行过程中是无法检测是否拥有权限的,导致app后续流程异常,甚至crash。
20 | 6. ...您来补充
21 |
22 | 对于上述问题,应该算是权限处理过程中最为难啃的骨头,这也是众多权限框架库想要致力于解决的问题,其中比较有名的应该是AndPermission,permission4m等,当然还有HaloPermission。
23 |
24 | 其实HaloPermission正是站在这些巨人的肩膀上产生的。但是很遗憾,对于上述的某些问题,就目前来说没有一个框架能够很好的解决,即使是目前比较有名的那些框架,HaloPermission深刻意识到了这个问题,因此HaloPermission在设计之初秉持一个理念"只有您自己才是最可靠的"。
25 | 也就是HaloPermission在设计上希望更合理,更容易配置,扩展,让每一个组件尽可能被重用,让代码更简单更可读,也就是说当遇到不可处理的一些问题时,您可以起到决定性作用。
26 |
27 |
28 | * 如何处理权限检测结果不准确的问题
29 |
30 | 如果通过权限检测得到的结果不准确(即通过 `*.checkSelfPermission`方法检测结果),那么我们可以通过对权限的实际使用来进一步确定权限是否可用,比如`Manifest.permission.WRITE_EXTERNAL_STORAGE`权限,假如我门在外置存储中创建一个文件成功,那说明app是具有`Manifest.permission.WRITE_EXTERNAL_STORAGE`权限的。
31 | * 支持6.0以前的版本
32 |
33 | 由前一个问题的答案可以得出,我们可以通过对权限的实际使用来进一步确定权限授权状态,如果没有被授权则可以打开权限设置界面让用户允许权限。
34 |
35 | ## HaloPermission的设计
36 | HaloPermission的设计将一次权限请求视为一个流程Processor,因此Processor=Request+PermissionCaller+PermissionChecker+Setting
37 |
38 | Request:权限请求的基本信息,也就是描述请求什么权限,回调,是否需要向用户解释,是否需要打开权限设置界面,以及权限设置界面的Intent是否自定义等。
39 |
40 | Processor:权限请求流程,对于权限请求来说,通常权限请求的业务流程通常是固定的,因此这部分的内容相对固定,不能进行扩展。
41 |
42 | PermissionCaller:权限请求者,也就是到底是以什么方式进行权限请求的,是利用透明的FragmentRxPermission的方式),还是透明Activity,还是使用当前上下文(Activity或Fragment)进行权限请求,由PermissionCaller决定,目前内置了RxPermission的形式,早期的版本其实提供了透明Activity的形式,但是使用觉得透明Activity的形式有点重。其次也提供了原始上下文,这一版本重构并未一并添加过来,如果有需求,则可以进行添加。
43 |
44 | PermissionChecker:权限校验者,其作用是为了在权限请求过程中以此为依据判定权限是否被授权,因此如果你觉得库里面写的权限校验规则不合理,你完全可以自己写一个Checker替换即可。目前内置了StandardChecker,即标准权限判断+AppOpsManagerCompat实现;还提供了一种StrictChecker校验模式,这个模式是在标准模式的基础上额外附加了一些严格权限校验(主要是为了6.0以下),因此Strict的模式并不是很建议使用,因为6.0以下又何必做这些呢,是有理由向Leader解释的。
45 | 因此各个组件的职责定义清晰,并且相对独立,所以如果你觉得那一部分不合胃口,完全可以想办法替换掉,至少其他部件是可以重复利用的。
46 |
47 | ## 特殊权限
48 | 目前支持未知来源、通知、通知渠道、悬浮窗、修改系统设置等。具体使用见说明文档。
49 |
50 | ## HaloPermission的具体使用
51 | [查看具体使用](https://github.com/SupLuo/HaloPermission/blob/master/doc/README_USAGE.md)
52 |
53 | ## HaloPermission靠谱么
54 | 至于这个问题,我想是需要您综合判断的,对于我而言,我觉得您才是最可靠的。
55 | 正如前面所说
56 | >其实HaloPermission正是站在这些巨人的肩膀上产生的。但是很遗憾,对于上述的某些问题,就目前来说没有一个框架能够很好的解决,即使是目前比较有名的那些框架,HaloPermission深刻意识到了这个问题,因此HaloPermission在设计之初秉持一个理念"只有您自己才是最可靠的"。
57 | 也就是HaloPermission在设计上希望更合理,更容易配置,扩展,让每一个组件尽可能被重用,让代码更简单更可读,也就是说当遇到不可处理的一些问题时,您可以起到决定性作用。
58 |
59 | HaloPermission目前使用的人不多,星星也少,但这并不意味着它不靠谱,毕竟Android权限这个问题出了这么久了,解决方案也有不少,程序员大多比较懒的,对于这种东西的抱有一个怀疑态度是很正常的,毕竟更换一个库还是需要从多方面考虑的,
60 | 其次这个库确实也才建立不久,还没有沉淀下来,**但是我的目的并不是为了推广这个库,而是我恰好也需要它,我也相信有很多人需要它,又恰好这样问题凭一己之力很难完成的很美好,所以我希望有其它人能共同建设**。
61 |
--------------------------------------------------------------------------------
/doc/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/doc/app-release.apk
--------------------------------------------------------------------------------
/doc/rationale_render.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/doc/rationale_render.gif
--------------------------------------------------------------------------------
/doc/setting_render.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uni-cstar/HaloPermission/45f3f0eac01bef8b3e0bbaaa149ac8fe01754020/doc/setting_render.gif
--------------------------------------------------------------------------------
/permission/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/permission/bintray.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.dcendents.android-maven'
2 | apply plugin: 'com.jfrog.bintray'
3 |
4 | def proj_version = '1.0.5-rc'
5 | def proj_artifact_id = 'permission'
6 | def proj_group = 'halo.android'
7 | def proj_pom_name = 'Android Permission'
8 | def proj_website_url = 'https://github.com/SupLuo/HaloPermission'
9 | def proj_vcs_url = 'https://github.com/SupLuo/HaloPermission.git'
10 |
11 |
12 | group = proj_group
13 | version = proj_version
14 |
15 | Properties properties = new Properties()
16 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
17 |
18 | install {
19 | repositories.mavenInstaller {
20 | // This generates POM.xml with proper parameters
21 | pom {
22 | project {
23 | groupId proj_group
24 | artifactId proj_artifact_id
25 | version proj_version
26 | packaging 'aar'
27 | name proj_pom_name
28 | url proj_website_url
29 |
30 | licenses {
31 | license {
32 | name properties.getOrDefault('PROJ_LICENCE_NAME','')
33 | url properties.getOrDefault('PROJ_LICENCE_URL','')
34 | distribution properties.getOrDefault('PROJ_LICENCE_DEST','')
35 | }
36 | }
37 |
38 | developers {
39 | developer {
40 | id properties.getOrDefault('DEVELOPER_ID','')
41 | name properties.getOrDefault('DEVELOPER_NAME','')
42 | email properties.getOrDefault('DEVELOPER_EMAIL','')
43 | }
44 | }
45 |
46 | scm {
47 | connection proj_vcs_url
48 | developerConnection proj_vcs_url
49 | url proj_website_url
50 | }
51 |
52 | }
53 | }
54 | }
55 | }
56 |
57 | task sourcesJar(type: Jar) {
58 | classifier = 'sources'
59 | from android.sourceSets.main.java.srcDirs
60 | }
61 |
62 | task javadoc(type: Javadoc) {
63 | // source = android.sourceSets.main.java.srcDirs
64 | // classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
65 | }
66 |
67 | task javadocJar(type: Jar, dependsOn: javadoc) {
68 | classifier = 'javadoc'
69 | from javadoc.destinationDir
70 | }
71 |
72 | javadoc {
73 | options{
74 | encoding "UTF-8"
75 | charSet 'UTF-8'
76 | author true
77 | version true
78 | links "http://docs.oracle.com/javase/7/docs/api"
79 | }
80 | }
81 |
82 | artifacts {
83 | archives javadocJar
84 | archives sourcesJar
85 | }
86 |
87 | bintray {
88 | user = properties.getProperty("bintray.user")
89 | key = properties.getProperty("bintray.apikey")
90 | configurations = ['archives']
91 | pkg {
92 | repo = "maven"
93 | name = proj_artifact_id // project name in jcenter
94 | websiteUrl = proj_website_url
95 | vcsUrl = proj_vcs_url
96 | licenses = ["Apache-2.0"]
97 | publish = true
98 | }
99 | }
--------------------------------------------------------------------------------
/permission/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 28
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 28
10 |
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 |
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 |
21 | sourceSets {
22 | main.java.srcDirs += 'src/main/kotlin'
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation "com.android.support:appcompat-v7:$support_version"
28 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
29 | }
30 |
31 |
32 | //tasks.getByPath(":permission:releaseAndroidJavadocs").enabled = false
33 |
34 | apply from:'./bintray.gradle'
35 |
36 |
--------------------------------------------------------------------------------
/permission/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/Lucio/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/permission/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/HaloPermission.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission
18 |
19 | import android.support.v4.app.Fragment
20 | import android.support.v4.app.FragmentActivity
21 | import halo.android.permission.caller.FragmentCaller
22 | import halo.android.permission.caller.PermissionCaller
23 | import halo.android.permission.checker.PermissionChecker
24 | import halo.android.permission.checker.StandardChecker
25 | import halo.android.permission.checker.StrictChecker
26 | import halo.android.permission.processor.PermissionProcessor
27 | import halo.android.permission.request.GrandAction
28 | import halo.android.permission.request.PermissionListener
29 | import halo.android.permission.request.PermissionRequest
30 |
31 | /**
32 | * Created by Lucio on 2019/6/23.
33 | */
34 |
35 | object HaloPermission {
36 |
37 | /**
38 | * 透明fragment请求方式
39 | */
40 | fun newFragmentCaller(activity: FragmentActivity): PermissionCaller {
41 | return FragmentCaller(activity)
42 | }
43 |
44 | /**
45 | * 透明fragment请求方式
46 | */
47 | fun newFragmentCaller(fragment: Fragment): PermissionCaller {
48 | return FragmentCaller(fragment)
49 | }
50 |
51 | /**
52 | * 标准权限检测
53 | */
54 | fun newStandardChecker(): PermissionChecker {
55 | return StandardChecker()
56 | }
57 |
58 | /**
59 | * 严格权限检测
60 | */
61 | @Deprecated(message = "感觉没什么必要")
62 | fun newStrictChecker(): PermissionChecker {
63 | return StrictChecker()
64 | }
65 |
66 | fun newRequest(vararg permissions: String): PermissionRequest {
67 | return PermissionRequest(permissions)
68 | }
69 |
70 |
71 | fun invokeRequest(activity: FragmentActivity,
72 | grandAction: GrandAction,
73 | vararg permissions: String) {
74 | newRequest(*permissions)
75 | .setGrandAction(grandAction)
76 | .request(activity)
77 | }
78 |
79 | fun invokeRequest(fragment: Fragment,
80 | grandAction: GrandAction,
81 | vararg permissions: String) {
82 | newRequest(*permissions)
83 | .setGrandAction(grandAction)
84 | .request(fragment)
85 | }
86 |
87 | fun invokeRequest(activity: FragmentActivity,
88 | permissionListener: PermissionListener,
89 | vararg permissions: String) {
90 | newRequest(*permissions)
91 | .setListener(permissionListener)
92 | .request(activity)
93 | }
94 |
95 | fun invokeRequest(fragment: Fragment,
96 | permissionListener: PermissionListener,
97 | vararg permissions: String) {
98 | newRequest(*permissions)
99 | .setListener(permissionListener)
100 | .request(fragment)
101 | }
102 |
103 |
104 | /**
105 | * 执行请求
106 | */
107 | fun invokeRequest(request: PermissionRequest,
108 | caller: PermissionCaller,
109 | checker: PermissionChecker) {
110 | PermissionProcessor(request, caller, checker)
111 | .invoke()
112 | }
113 |
114 | fun invokeRequest(activity: FragmentActivity,
115 | request: PermissionRequest) {
116 | return request.request(activity)
117 | }
118 |
119 | fun invokeRequest(fragment: Fragment,
120 | request: PermissionRequest) {
121 | return request.request(fragment)
122 | }
123 |
124 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/HaloSpecPermission.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission
18 |
19 | import android.Manifest
20 | import android.annotation.SuppressLint
21 | import android.app.AppOpsManager
22 | import android.app.NotificationManager
23 | import android.content.Context
24 | import android.content.Intent
25 | import android.os.Binder
26 | import android.os.Build
27 | import android.provider.Settings
28 | import android.support.v4.app.Fragment
29 | import android.support.v4.app.FragmentActivity
30 | import android.support.v4.app.NotificationManagerCompat
31 | import halo.android.permission.common.*
32 | import halo.android.permission.spec.*
33 |
34 | /**
35 | * Created by Lucio on 2019/6/24.
36 | */
37 |
38 | object HaloSpecPermission {
39 |
40 | /**
41 | * 是否拥有悬浮窗权限
42 | */
43 | @JvmStatic
44 | @SuppressLint("InlinedApi")
45 | fun areDrawOverlaysEnable(ctx: Context): Boolean {
46 | val sdkInt = Build.VERSION.SDK_INT
47 | if (sdkInt >= Util.M) {
48 | val result = Settings.canDrawOverlays(ctx)
49 | PLog.d("[areDrawOverlaysEnable]:6.0之后的版本,检测结果=$result")
50 | return result
51 | } else if (sdkInt >= Util.KITKAT) {
52 | try {
53 | val appOpsManager = ctx.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
54 |
55 | val method = AppOpsManager::class.java.getDeclaredMethod("checkOp", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType, String::class.java)
56 | val result = 0 == method.invoke(appOpsManager, 24, Binder.getCallingUid(), ctx.packageName) as Int
57 | PLog.d("[areDrawOverlaysEnable]:4.4-6.0之间的版本,调用反射检测权限成功,result=$result")
58 | return result
59 | } catch (e: Exception) {
60 | PLog.d("[areDrawOverlaysEnable]:4.4-6.0之间的版本,调用反射检测权限失败,返回true")
61 | return true
62 | }
63 | } else {
64 | PLog.d("[areDrawOverlaysEnable]:4.4之前版本,默认返回true")
65 | //4.4之前直接使用:部分定制系统厂商自己处理的可能无效
66 | return true
67 | }
68 | }
69 |
70 | /**
71 | * 是否拥有悬浮窗权限
72 | * @throws Need to declare android.permission.SYSTEM_ALERT_WINDOW to call this api,
73 | * 需要在清单文件中申明android.permission.SYSTEM_ALERT_WINDOW,否则抛出异常
74 | */
75 | @JvmStatic
76 | @Throws(SecurityException::class)
77 | fun areDrawOverlaysEnableOrThrow(ctx: Context): Boolean {
78 | if (Build.VERSION.SDK_INT >= Util.M && !Util.isManifestPermission(ctx, Manifest.permission.SYSTEM_ALERT_WINDOW)) {
79 | throw SecurityException("Need to declare android.permission.SYSTEM_ALERT_WINDOW to call this api." +
80 | "需要在Manifest文件中申明android.permission.SYSTEM_ALERT_WINDOW权限,否则无法修改。")
81 | }
82 | return areDrawOverlaysEnable(ctx)
83 | }
84 |
85 | /**
86 | * 创建设置悬浮窗权限意图
87 | */
88 | @JvmStatic
89 | fun createDrawOverlaysSettingIntentOrDefault(ctx: Context): Intent {
90 | if (Build.VERSION.SDK_INT >= Util.M) {
91 | return IntentSupport.createDrawOverlaysSettingIntent(ctx).orDefault(ctx)
92 | } else {
93 | return IntentSupport.createAppDetailSettingIntent(ctx).orDefault(ctx)
94 | }
95 | }
96 |
97 | /**
98 | * 请求悬浮窗权限
99 | */
100 | @JvmStatic
101 | fun requestDrawOverlays(activity: FragmentActivity, listener: SpecialListener) {
102 | checkAndRequest(activity, SpecPermissionPackageInstall(listener))
103 | }
104 |
105 | /**
106 | * 请求悬浮窗权限
107 | */
108 | @JvmStatic
109 | fun requestDrawOverlays(fragment: Fragment, listener: SpecialListener) {
110 | checkAndRequest(fragment, SpecPermissionPackageInstall(listener))
111 | }
112 |
113 | /**
114 | * 是否能够更改系统设置
115 | * manifest必须配置:android.Manifest.permission#WRITE_SETTINGS
116 | */
117 | @JvmStatic
118 | fun areWriteSystemSettingEnable(ctx: Context): Boolean {
119 | //23以前默认没有权限
120 | if (Build.VERSION.SDK_INT < Util.M)
121 | return true
122 | return Settings.System.canWrite(ctx)
123 | }
124 |
125 | /**
126 | * 是否能够更改系统设置
127 | * @throws Need to declare android.permission.WRITE_SETTINGS to call this api,
128 | * 需要在清单文件中申明android.permission.WRITE_SETTINGS,否则抛出异常
129 | */
130 | @JvmStatic
131 | fun areWriteSystemSettingEnableOrThrow(ctx: Context): Boolean {
132 | //23以前默认没有权限
133 | if (Build.VERSION.SDK_INT >= Util.M && !Util.isManifestPermission(ctx, Manifest.permission.WRITE_SETTINGS)) {
134 | throw SecurityException("Need to declare android.permission.WRITE_SETTINGS to call this api,\n" +
135 | "需要在清单文件中申明android.permission.WRITE_SETTINGS权限,否则无法修改设置")
136 | }
137 | return areWriteSystemSettingEnable(ctx)
138 | }
139 |
140 | /**
141 | * 创先系统设置修改意图
142 | */
143 | @JvmStatic
144 | fun createWriteSystemSettingIntentOrDefault(ctx: Context): Intent {
145 | if (Build.VERSION.SDK_INT >= Util.M) {
146 | return IntentSupport.createWriteSystemSettingIntent(ctx).orDefault(ctx)
147 | } else {
148 | return IntentSupport.createAppDetailSettingIntent(ctx).orDefault(ctx)
149 | }
150 | }
151 |
152 | /**
153 | * 请求系统设置修改权限
154 | */
155 | @JvmStatic
156 | fun requestWriteSystemSetting(activity: FragmentActivity, listener: SpecialListener) {
157 | checkAndRequest(activity, SpecPermissionWriteSystemSetting(listener))
158 | }
159 |
160 | /**
161 | * 请求系统设置修改权限
162 | */
163 | @JvmStatic
164 | fun requestWriteSystemSetting(fragment: Fragment, listener: SpecialListener) {
165 | checkAndRequest(fragment, SpecPermissionWriteSystemSetting(listener))
166 | }
167 |
168 | /**
169 | * 是否能够请求应用安装
170 | * @throws Need to declare android.permission.REQUEST_INSTALL_PACKAGES to call this api,需要在清单文件中添加以下权限申明
171 | */
172 | @JvmStatic
173 | @Throws(SecurityException::class)
174 | fun areRequestPackageInstallsEnable(ctx: Context): Boolean {
175 | //8.0之前不需要适配未知来源 应用安装
176 | if (Build.VERSION.SDK_INT < Util.O)
177 | return true
178 | return ctx.packageManager.canRequestPackageInstalls()
179 | }
180 |
181 | /**
182 | * 意图:当前应用的"未知来源管理"
183 | */
184 | @JvmStatic
185 | fun createAppUnknownSourceManagerIntentOrDefault(ctx: Context): Intent {
186 | if (Build.VERSION.SDK_INT >= Util.O) {
187 | return IntentSupport.createAppUnknownSourceManagerIntent(ctx).orDefault(ctx)
188 | } else {
189 | return IntentSupport.createAppDetailSettingIntent(ctx).orSystemSettingIntent(ctx)
190 | }
191 | }
192 |
193 | /**
194 | * 请求"未知来源管理"权限
195 | * @throws Need to declare android.permission.REQUEST_INSTALL_PACKAGES to call this api,需要在清单文件中添加以下权限申明
196 | */
197 | @JvmStatic
198 | fun requestAppUnknownSource(activity: FragmentActivity, listener: SpecialListener) {
199 | checkAndRequest(activity, SpecPermissionPackageInstall(listener))
200 | }
201 |
202 | /**
203 | * 请求"未知来源管理"权限
204 | */
205 | @JvmStatic
206 | fun requestAppUnknownSource(fragment: Fragment, listener: SpecialListener) {
207 | checkAndRequest(fragment, SpecPermissionPackageInstall(listener))
208 | }
209 |
210 | /**
211 | * 通知是否已开启
212 | */
213 | @JvmStatic
214 | fun areNotificationEnabled(ctx: Context): Boolean {
215 | return NotificationManagerCompat.from(ctx.applicationContext)
216 | .areNotificationsEnabled()
217 | }
218 |
219 | /**
220 | * 创建通知设置意图,或默认意图
221 | */
222 | @JvmStatic
223 | fun createNotificationSettingIntentOrDefault(ctx: Context): Intent {
224 | return IntentSupport.createNotificationSettingIntent(ctx)
225 | .orDefault(ctx)
226 | }
227 |
228 | /**
229 | * 请求通知权限
230 | */
231 | @JvmStatic
232 | fun requestNotification(activity: FragmentActivity, listener: SpecialListener) {
233 | checkAndRequest(activity, SpecPermissionNotification(listener))
234 | }
235 |
236 | /**
237 | * 请求通知权限
238 | */
239 | @JvmStatic
240 | fun requestNotification(fragment: Fragment, listener: SpecialListener) {
241 | checkAndRequest(fragment, SpecPermissionNotification(listener))
242 | }
243 |
244 | /**
245 | * 通知渠道是否可用
246 | */
247 | @JvmStatic
248 | fun areNotificationChannelsEnabled(ctx: Context, channelId: String): Boolean {
249 | try {
250 | return areNotificationChannelsEnabledOrThrow(ctx, channelId)
251 | } catch (e: Exception) {
252 | return false
253 | }
254 | }
255 |
256 | @JvmStatic
257 | fun areNotificationChannelsEnabledOrThrow(ctx: Context, channelId: String): Boolean {
258 | if (Build.VERSION.SDK_INT < Util.O)
259 | return true
260 | val notificationChannel = ctx.getSystemService(NotificationManager::class.java)
261 | .getNotificationChannel(channelId)
262 | //渠道为空或者渠道优先级不为none则视为拥有权限
263 |
264 | return notificationChannel == null || notificationChannel.importance != NotificationManager.IMPORTANCE_NONE
265 | }
266 |
267 | /**
268 | * 创建通知渠道设置意图
269 | */
270 | @JvmStatic
271 | fun createNotificationChanelSettingIntentOrDefault(ctx: Context, channelId: String): Intent {
272 | return IntentSupport.createNotificationChanelSettingIntent(ctx, channelId)
273 | .orDefault(ctx)
274 | }
275 |
276 | /**
277 | * 请求通知渠道权限
278 | */
279 | @JvmStatic
280 | fun requestNotificationChanel(fragmentActivity: FragmentActivity, channelId: String, listener: SpecialListener) {
281 | HaloSpecPermission.checkAndRequest(fragmentActivity, SpecPermissionNotificationChannel(listener, channelId))
282 | }
283 |
284 | /**
285 | * 请求通知渠道权限
286 | */
287 | @JvmStatic
288 | fun requestNotificationChanel(fragment: Fragment, channelId: String, listener: SpecialListener) {
289 | HaloSpecPermission.checkAndRequest(fragment, SpecPermissionNotificationChannel(listener, channelId))
290 | }
291 |
292 | @JvmStatic
293 | private fun checkAndRequest(fragment: Fragment, spec: SpecPermission) {
294 | if (spec.isGrandOrThrow(fragment.requireContext())) {
295 | spec.notifyGrand()
296 | } else {
297 | spec.listener.showRationalView(object : SpecialListener.Process {
298 | override fun onSpecialNext() {
299 | SpecPermissionFragmentCaller(fragment)
300 | .requestSpecialPermission(spec)
301 | }
302 |
303 | override fun onSpecialCancel() {
304 | //nothing
305 | }
306 | })
307 |
308 | }
309 | }
310 |
311 | @JvmStatic
312 | private fun checkAndRequest(fragmentActivity: FragmentActivity, spec: SpecPermission) {
313 | if (spec.isGrandOrThrow(fragmentActivity)) {
314 | spec.notifyGrand()
315 | } else {
316 | spec.listener.showRationalView(object : SpecialListener.Process {
317 | override fun onSpecialNext() {
318 | SpecPermissionFragmentCaller(fragmentActivity)
319 | .requestSpecialPermission(spec)
320 | }
321 |
322 | override fun onSpecialCancel() {
323 | //nothing
324 | }
325 | })
326 |
327 | }
328 | }
329 |
330 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/caller/PermissionCaller.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.caller
18 |
19 | import android.content.Context
20 | import android.content.Intent
21 | import halo.android.permission.processor.PermissionResponder
22 | import halo.android.permission.setting.SettingResponder
23 |
24 | /**
25 | * Created by Lucio on 2019/6/22.
26 | */
27 |
28 | interface PermissionCaller {
29 |
30 | val ctx:Context
31 | /**
32 | * @param responder 回调
33 | * @param permissions 权限
34 | */
35 | fun requestPermission(responder: PermissionResponder, vararg permissions: String)
36 |
37 | fun requestPermissionSetting(responder: SettingResponder,intent: Intent)
38 |
39 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/caller/PermissionFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.caller
18 |
19 | import android.content.Context
20 | import android.content.Intent
21 | import android.os.Bundle
22 | import android.support.v4.app.Fragment
23 | import halo.android.permission.common.PLog
24 | import halo.android.permission.processor.PermissionResponder
25 | import halo.android.permission.setting.SettingResponder
26 | import halo.android.permission.spec.SpecPermission
27 | import halo.android.permission.spec.SpecialCaller
28 |
29 | class PermissionFragment : Fragment(), PermissionCaller, SpecialCaller {
30 |
31 | override val ctx: Context
32 | get() = this.requireContext()
33 |
34 |
35 | private var mPermissionResponder: PermissionResponder? = null
36 | private val mPermissionRequestCode = 11
37 |
38 | private var mPermissionSettingResponder: SettingResponder? = null
39 | private val mPermissionSettingRequestCode = 12
40 |
41 | private var mSpecPermission: SpecPermission? = null
42 | private val mSpecRequestCode = 13
43 |
44 | override fun onCreate(savedInstanceState: Bundle?) {
45 | super.onCreate(savedInstanceState)
46 | this.retainInstance = true
47 | }
48 |
49 | override fun requestPermission(responder: PermissionResponder, vararg permissions: String) {
50 | mPermissionResponder = responder
51 | PLog.d("invokeRequest permission from PermissionFragment:\n" + permissions.joinToString("\n"))
52 | requestPermissions(permissions, mPermissionRequestCode)
53 | }
54 |
55 | override fun requestPermissionSetting(responder: SettingResponder, intent: Intent) {
56 | mPermissionSettingResponder = responder
57 | PLog.d("invokeRequest permission setting from PermissionFragment:\n$intent")
58 | startActivityForResult(intent, mPermissionSettingRequestCode)
59 | }
60 |
61 | override fun requestSpecialPermission(spec: SpecPermission) {
62 | mSpecPermission = spec
63 | startActivityForResult(spec.createSettingIntent(requireContext()), mSpecRequestCode)
64 | }
65 |
66 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
67 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
68 | if (requestCode == mPermissionRequestCode) {
69 | mPermissionResponder?.onPermissionResponderResult(permissions, grantResults)
70 | mPermissionResponder = null
71 | }
72 | PLog.d("invokeRequest permission from PermissionFragment:\n" + permissions.joinToString("\n"))
73 | }
74 |
75 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
76 | super.onActivityResult(requestCode, resultCode, data)
77 | if (requestCode == mPermissionSettingRequestCode) {
78 | mPermissionSettingResponder?.onSettingResponderResult(resultCode, data)
79 | mPermissionSettingResponder = null
80 | } else if (requestCode == mSpecRequestCode) {
81 | mSpecPermission?.apply {
82 | if(isGrand(requireContext())){
83 | notifyGrand()
84 | }else{
85 | notifyDeny()
86 | }
87 | }
88 | mSpecPermission = null
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/caller/RuntimePermissionFragmentCaller.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.caller
18 |
19 | import android.content.Context
20 | import android.content.Intent
21 | import android.support.v4.app.Fragment
22 | import android.support.v4.app.FragmentActivity
23 | import android.support.v4.app.FragmentManager
24 | import halo.android.permission.processor.PermissionResponder
25 | import halo.android.permission.setting.SettingResponder
26 |
27 | /**
28 | * Created by Lucio on 2019/6/22.
29 | */
30 |
31 |
32 | open class FragmentCaller(override val ctx: Context, val fm: FragmentManager) : PermissionCaller {
33 |
34 | private val TAG = FragmentCaller::class.java.simpleName
35 |
36 | protected val callerFragment: PermissionFragment by lazy {
37 | var instance: PermissionFragment? = findPermissionFragment()
38 | if (instance == null) {
39 | instance = PermissionFragment()
40 | fm.beginTransaction().add(instance, TAG).commitNow()
41 | }
42 | instance!!
43 | }
44 |
45 | constructor(activity: FragmentActivity) : this(activity, activity.supportFragmentManager)
46 |
47 | constructor(fragment: Fragment) : this(fragment.requireContext(), fragment.childFragmentManager)
48 |
49 | private fun findPermissionFragment(): PermissionFragment? {
50 | return fm.findFragmentByTag(TAG) as? PermissionFragment
51 | }
52 |
53 | override fun requestPermission(responder: PermissionResponder, vararg permissions: String) {
54 | callerFragment.requestPermission(responder, *permissions)
55 | }
56 |
57 | override fun requestPermissionSetting(responder: SettingResponder, intent: Intent) {
58 | callerFragment.requestPermissionSetting(responder, intent)
59 | }
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/PermissionChecker.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker
18 |
19 | import android.content.Context
20 | import halo.android.permission.common.Util
21 |
22 | /**
23 | * Created by Lucio on 2019/6/22.
24 | */
25 |
26 | interface PermissionChecker{
27 |
28 | fun isPermissionGranted(ctx: Context, permission: String): Boolean
29 |
30 | fun shouldShowRequestPermissionRationale(ctx:Context,permission: String): Boolean {
31 | return Util.shouldShowRequestPermissionRationale(ctx, permission)
32 | }
33 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/StandardChecker.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * Created by Lucio on 18/4/8.
19 | *
20 | */
21 | package halo.android.permission.checker
22 |
23 | import android.content.Context
24 | import android.support.v4.app.AppOpsManagerCompat
25 | import halo.android.permission.common.Util
26 |
27 | /**
28 | * 标准权限判断
29 | */
30 | open class StandardChecker : PermissionChecker {
31 |
32 | override fun isPermissionGranted(ctx: Context, permission: String): Boolean {
33 | if (Util.isPermissionGranted(ctx, permission)) {
34 | //以下代码仿照v4中的PermissionChecker实现
35 | //将Permission转换成对应的Op
36 | val op = AppOpsManagerCompat.permissionToOp(permission)
37 | if (!op.isNullOrEmpty()) {
38 | //如果AppOpsManagerCompat.noteProxyOp(ctx, op, ctx.packageName) == PERMISSION_DENIED_APP_OP
39 | // 则表示The permission is denied because the app op is not allowed.
40 | return AppOpsManagerCompat.noteProxyOp(ctx, op, ctx.packageName) == AppOpsManagerCompat.MODE_ALLOWED
41 | }
42 | return true
43 | } else {
44 | return false
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/StrictChecker.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker
18 |
19 | import android.Manifest
20 | import android.content.Context
21 | import halo.android.permission.checker.strict.*
22 |
23 |
24 | /**
25 | * Created by Lucio on 18/4/12.
26 | * 严格权限校验
27 | */
28 |
29 | class StrictChecker : StandardChecker() {
30 |
31 | override fun isPermissionGranted(ctx: Context, permission: String): Boolean {
32 | return super.isPermissionGranted(ctx, permission) && Factory.create(ctx, permission).check()
33 | }
34 |
35 | internal companion object Factory {
36 |
37 | /**
38 | * API 16才有这两个变量
39 | */
40 | const val READ_CALL_LOG = "android.permission.READ_CALL_LOG"
41 | const val WRITE_CALL_LOG = "android.permission.WRITE_CALL_LOG"
42 | const val READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"
43 |
44 | /**
45 | * API 20才的变量
46 | */
47 | const val BODY_SENSORS = "android.permission.BODY_SENSORS"
48 |
49 | @JvmStatic
50 | fun create(ctx: Context, permission: String): BaseCheck {
51 | return when (permission) {
52 | Manifest.permission.READ_CONTACTS -> ReadContactsCheck(ctx)
53 | Manifest.permission.WRITE_CONTACTS -> WriteContactsCheck(ctx)
54 | READ_EXTERNAL_STORAGE -> ReadExternalStorageCheck(ctx)
55 | Manifest.permission.WRITE_EXTERNAL_STORAGE -> WriteExternalStorageCheck(ctx)
56 | Manifest.permission.READ_CALENDAR -> ReadCalendarCheck(ctx)
57 | Manifest.permission.WRITE_CALENDAR -> WriteCalendarCheck(ctx)
58 | READ_CALL_LOG -> ReadCallLogCheck(ctx)
59 | WRITE_CALL_LOG -> WriteCallLogCheck(ctx)
60 | Manifest.permission.ACCESS_COARSE_LOCATION,
61 | Manifest.permission.ACCESS_FINE_LOCATION -> LocationCheck(ctx)
62 | Manifest.permission.CAMERA -> {
63 | // if (Build.VERSION.SDK_INT >= 21) {
64 | // CameraCheck21(ctx)
65 | // } else {
66 | // CameraCheck(ctx)
67 | // }
68 | CameraCheck(ctx)
69 | }
70 | Manifest.permission.RECORD_AUDIO -> RecordAudioCheck(ctx)
71 | BODY_SENSORS -> BodySensorCheck(ctx)
72 | Manifest.permission.READ_PHONE_STATE -> ReadPhoneStateCheck(ctx)
73 | Manifest.permission.READ_SMS -> ReadSMSCheck(ctx)
74 | Manifest.permission.SEND_SMS,
75 | Manifest.permission.RECEIVE_MMS,
76 | Manifest.permission.RECEIVE_SMS -> NormalCheck(ctx, permission)
77 | else -> NormalCheck(ctx, permission)
78 | }
79 | }
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/BaseCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import android.database.Cursor
21 |
22 |
23 | /**
24 | * Created by Lucio on 18/4/24.
25 | */
26 |
27 | abstract class BaseCheck(val ctx: Context) {
28 |
29 | abstract fun check(): Boolean
30 |
31 | protected inline fun tryCheck(check: () -> Boolean): Boolean {
32 | return try {
33 | check()
34 | } catch (e: Exception) {
35 | false
36 | }
37 | }
38 |
39 | protected inline fun tryIgnore(block: () -> Unit) {
40 | try {
41 | block()
42 | } catch (e: Exception) {
43 | }
44 | }
45 |
46 | protected fun tryReadCursorData(cursor: Cursor?): Boolean {
47 | if (cursor != null) {
48 | try {
49 | if (cursor.moveToFirst()) {//cursor is not empty,try read data
50 | val columnIndex = 0
51 | val type = cursor.getType(columnIndex)
52 | when (type) {
53 | Cursor.FIELD_TYPE_BLOB -> {
54 | cursor.getBlob(columnIndex)
55 | }
56 | Cursor.FIELD_TYPE_NULL -> {
57 | cursor.isNull(columnIndex)
58 | }
59 | Cursor.FIELD_TYPE_INTEGER, Cursor.FIELD_TYPE_FLOAT, Cursor.FIELD_TYPE_STRING -> {
60 | cursor.getString(0)
61 | }
62 | else -> {
63 | cursor.getString(0)
64 | }
65 | }
66 | return true
67 | } else {
68 | //count=0 可能是因为用户拒绝权限,也可能是用户数据真的为空,但考虑实际情况数据很少为空,所以为0视为没有权限,不为0视为有权限但移动cursor出错
69 | return cursor.count != 0
70 | }
71 | } catch (e: Exception) {
72 | e.printStackTrace()
73 | return false // cause exception when read,regard as read failed
74 | } finally {
75 | if (!cursor.isClosed)
76 | cursor.close()
77 | }
78 | } else {//cursor is null,regard as read failed
79 | return false
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/BodySensorCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import android.hardware.Sensor
21 | import android.hardware.SensorEvent
22 | import android.hardware.SensorEventListener
23 | import android.hardware.SensorManager
24 | import android.os.Build
25 |
26 | /**
27 | * Created by Lucio on 18/4/25.
28 | * 身体传感器权限
29 | */
30 | class BodySensorCheck(ctx: Context) : BaseCheck(ctx), SensorEventListener {
31 |
32 | override fun check(): Boolean = tryCheck {
33 | if(Build.VERSION.SDK_INT < 20)
34 | return true
35 |
36 | val sensorManager = ctx.getSystemService(Context.SENSOR_SERVICE) as SensorManager
37 | val heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE)
38 | if (heartRateSensor != null) {
39 | //尝试绑定回调,再移除
40 | sensorManager.registerListener(this, heartRateSensor, 3)
41 | sensorManager.unregisterListener(this)
42 | }
43 | return true
44 | }
45 |
46 | override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
47 |
48 | }
49 |
50 | override fun onSensorChanged(event: SensorEvent?) {
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/CameraCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package halo.android.permission.checker.strict
17 |
18 | import android.content.Context
19 | import android.hardware.Camera
20 |
21 | /**
22 | * Created by Lucio on 18/4/25.
23 | */
24 | class CameraCheck(ctx: Context) : BaseCheck(ctx) {
25 |
26 | override fun check(): Boolean = tryCheck {
27 | var mCamera: Camera? = null
28 | try {
29 | mCamera = Camera.open(0)
30 | return mCamera != null
31 | } catch (e: Exception) {
32 | e.printStackTrace()
33 | return false
34 | } finally {
35 | if (mCamera != null) {
36 | mCamera.release()
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/CameraCheck21.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package halo.android.permission.checker.strict
17 |
18 | import android.Manifest
19 | import android.annotation.TargetApi
20 | import android.content.Context
21 | import android.hardware.camera2.CameraDevice
22 | import android.hardware.camera2.CameraManager
23 | import android.os.Build
24 | import android.support.annotation.RequiresPermission
25 |
26 | /**
27 | * Created by Lucio on 18/4/25.
28 | */
29 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
30 | @Deprecated("异步检测结果,待测试")
31 | class CameraCheck21(ctx: Context) : BaseCheck(ctx){
32 |
33 |
34 | private val callBack = object : CameraDevice.StateCallback() {
35 | override fun onOpened(camera: CameraDevice?) {
36 | if (camera != null) {
37 | camera.close()
38 | }
39 | }
40 |
41 | override fun onDisconnected(camera: CameraDevice?) {
42 |
43 | }
44 |
45 | override fun onError(camera: CameraDevice?, error: Int) {
46 | }
47 | }
48 |
49 | @RequiresPermission(Manifest.permission.CAMERA)
50 | override fun check(): Boolean = tryCheck{
51 | val cameraManager = ctx.getSystemService(Context.CAMERA_SERVICE) as CameraManager
52 | val cameraIds = cameraManager.cameraIdList
53 | if (cameraIds.isEmpty())
54 | return false
55 | val cameraId = cameraIds[0]
56 | cameraManager.openCamera(cameraId, callBack, null)
57 | return true
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/LocationCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.Manifest
20 | import android.annotation.SuppressLint
21 | import android.content.Context
22 | import android.location.Location
23 | import android.location.LocationListener
24 | import android.location.LocationManager
25 | import android.os.Bundle
26 | import android.support.annotation.RequiresPermission
27 |
28 | /**
29 | * Created by Lucio on 18/4/25.
30 | * 定位权限
31 | */
32 | class LocationCheck(ctx: Context) : BaseCheck(ctx) {
33 |
34 | @RequiresPermission(anyOf = arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,
35 | Manifest.permission.ACCESS_FINE_LOCATION))
36 | override fun check(): Boolean = tryCheck {
37 | val locationManager = ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager
38 | val list = locationManager.getProviders(true)
39 |
40 | if (list.contains(LocationManager.GPS_PROVIDER)) {
41 | return true
42 | } else if (list.contains(LocationManager.NETWORK_PROVIDER)) {
43 | return true
44 | } else {
45 | locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0L, 0f,
46 | @SuppressLint("MissingPermission")
47 | object : LocationListener {
48 | override fun onLocationChanged(location: Location?) {
49 | locationManager.removeUpdates(this)
50 | }
51 |
52 | override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
53 | locationManager.removeUpdates(this)
54 | }
55 |
56 | override fun onProviderEnabled(provider: String?) {
57 | locationManager.removeUpdates(this)
58 | }
59 |
60 | override fun onProviderDisabled(provider: String?) {
61 | locationManager.removeUpdates(this)
62 | }
63 |
64 | })
65 | }
66 | return true
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/NormalCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import halo.android.permission.checker.StandardChecker
21 |
22 | /**
23 | * Created by Lucio on 18/4/24.
24 | */
25 |
26 | class NormalCheck(ctx:Context,val permission:String) : BaseCheck(ctx){
27 | override fun check(): Boolean {
28 | return StandardChecker().isPermissionGranted(ctx,permission)
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/ReadCalendarCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.Manifest
20 | import android.content.Context
21 | import android.os.Build
22 | import android.provider.CalendarContract
23 | import android.support.annotation.RequiresPermission
24 |
25 | /**
26 | * Created by Lucio on 18/4/24.
27 | * 检查 日历 读权限
28 | */
29 | class ReadCalendarCheck(ctx: Context) : BaseCheck(ctx) {
30 |
31 | @RequiresPermission(Manifest.permission.READ_CALENDAR)
32 | override fun check(): Boolean = tryCheck {
33 | if (Build.VERSION.SDK_INT >= 14) {
34 | val projection = arrayOf(CalendarContract.Calendars._ID, CalendarContract.Calendars.NAME)
35 | val cursor = ctx.contentResolver.query(CalendarContract.Calendars.CONTENT_URI, projection, null, null, null)
36 | return tryReadCursorData(cursor)
37 | } else {
38 | return false
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/ReadCallLogCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import android.provider.CallLog
21 | import android.support.annotation.RequiresPermission
22 | import halo.android.permission.checker.StrictChecker
23 |
24 | /**
25 | * Created by Lucio on 18/4/24.
26 | * 检查 读取通话记录 权限
27 | */
28 | class ReadCallLogCheck(ctx: Context) : BaseCheck(ctx) {
29 |
30 | @RequiresPermission(StrictChecker.READ_CALL_LOG)
31 | override fun check(): Boolean = tryCheck {
32 | // if (Build.VERSION.SDK_INT < 16)
33 | // return@tryCheck true
34 | val projection = arrayOf(CallLog.Calls._ID, CallLog.Calls.NUMBER, CallLog.Calls.TYPE)
35 | val cursor = ctx.contentResolver.query(CallLog.Calls.CONTENT_URI, projection, null, null, null)
36 | return tryReadCursorData(cursor)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/ReadContactsCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import android.provider.ContactsContract
21 |
22 | /**
23 | * Created by Lucio on 18/4/24.
24 | * 检查读取联系人权限
25 | */
26 | class ReadContactsCheck(ctx: Context) : BaseCheck(ctx) {
27 |
28 | override fun check(): Boolean = tryCheck {
29 | val projection = arrayOf(ContactsContract.Data._ID, ContactsContract.Data.DATA1)
30 | val cursor = ctx.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null)
31 | return tryReadCursorData(cursor)
32 | }
33 |
34 |
35 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/ReadExternalStorageCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import android.os.Environment
21 |
22 |
23 | /**
24 | * Created by Lucio on 18/4/24.
25 | * 检测 外置存储是否可读
26 | */
27 |
28 | class ReadExternalStorageCheck(ctx: Context) : BaseCheck(ctx) {
29 |
30 | override fun check(): Boolean = tryCheck {
31 | if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED)
32 | // sd卡不存在
33 | return true
34 |
35 | val directory = Environment.getExternalStorageDirectory()
36 | return directory.exists() && directory.canRead()
37 | }
38 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/ReadPhoneStateCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.Manifest
20 | import android.annotation.SuppressLint
21 | import android.content.Context
22 | import android.support.annotation.RequiresPermission
23 |
24 | /**
25 | * Created by Lucio on 18/4/24.
26 | * 检查 日历 读权限
27 | */
28 | class ReadPhoneStateCheck(ctx: Context) : BaseCheck(ctx) {
29 |
30 | @SuppressLint("HardwareIds")
31 | @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
32 | override fun check(): Boolean = tryCheck {
33 | val service = ctx.getSystemService(Context.TELEPHONY_SERVICE) as android.telephony.TelephonyManager
34 | return !service.deviceId.isNullOrEmpty() || !service.subscriberId.isNullOrEmpty()
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/ReadSMSCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import android.os.Build
21 | import android.provider.Telephony
22 |
23 | /**
24 | * Created by Lucio on 18/4/24.
25 | * 检测 短信 是否可读
26 | * 备注:在API19以前,默认返回true
27 | */
28 | class ReadSMSCheck(ctx: Context) : BaseCheck(ctx) {
29 |
30 | override fun check(): Boolean = tryCheck {
31 | if (Build.VERSION.SDK_INT >= 19) {
32 | val projection = arrayOf(Telephony.Sms._ID, Telephony.Sms.ADDRESS, Telephony.Sms.PERSON, Telephony.Sms.BODY)
33 | val cursor = ctx.contentResolver.query(Telephony.Sms.CONTENT_URI, projection, null, null, null)
34 | return tryReadCursorData(cursor)
35 | } else {
36 | return true
37 | }
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/RecordAudioCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import android.media.MediaRecorder
21 | import java.io.File
22 |
23 | /**
24 | * Created by Lucio on 18/4/25.
25 | * 录音权限检测
26 | */
27 | class RecordAudioCheck(ctx: Context) : BaseCheck(ctx) {
28 |
29 | override fun check(): Boolean = tryCheck {
30 | var mr: MediaRecorder? = null
31 | var tempFile: File? = null
32 | try {
33 | mr = MediaRecorder()
34 | tempFile = File.createTempFile("RecordAudioCheck", "test")
35 |
36 | mr.run {
37 | setAudioSource(MediaRecorder.AudioSource.MIC)
38 | setOutputFormat(MediaRecorder.OutputFormat.AMR_NB)
39 | setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
40 | setOutputFile(tempFile.absolutePath)
41 | prepare()
42 | start()
43 | }
44 | return true
45 | } finally {//清除测试资源
46 | //释放MediaRecorder
47 | mr?.run {
48 | tryIgnore {
49 | stop()
50 | }
51 |
52 | tryIgnore {
53 | release()
54 | }
55 | }
56 |
57 | // 删除临时文件
58 | tempFile?.run {
59 | if (exists()) {
60 | delete()
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/WriteCalendarCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.Manifest
20 | import android.content.ContentUris
21 | import android.content.ContentValues
22 | import android.content.Context
23 | import android.graphics.Color
24 | import android.os.Build
25 | import android.provider.CalendarContract
26 | import android.support.annotation.RequiresPermission
27 | import java.util.*
28 |
29 | /**
30 | * Created by Lucio on 18/4/24.
31 | * 检查 日历 写权限
32 | */
33 | class WriteCalendarCheck(ctx: Context) : BaseCheck(ctx) {
34 |
35 | private val name = "WriteCalendarCheck"
36 | private val account = "HaloPermission@github.com"
37 | @RequiresPermission(Manifest.permission.READ_CALENDAR)
38 | override fun check(): Boolean = tryCheck {
39 |
40 | if (Build.VERSION.SDK_INT <= 14)
41 | return@tryCheck true
42 | try {
43 | val timeZone = TimeZone.getDefault()
44 | val value = ContentValues()
45 | value.put(CalendarContract.Calendars.NAME, name)
46 | value.put(CalendarContract.Calendars.ACCOUNT_NAME, account)
47 | value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL)
48 | value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, name)
49 | value.put(CalendarContract.Calendars.VISIBLE, 1)
50 | value.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.BLUE)
51 | value.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER)
52 | value.put(CalendarContract.Calendars.SYNC_EVENTS, 1)
53 | value.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timeZone.id)
54 | value.put(CalendarContract.Calendars.OWNER_ACCOUNT, name)
55 | value.put(CalendarContract.Calendars.CAN_ORGANIZER_RESPOND, 0)
56 |
57 | val insertUri = CalendarContract.Calendars.CONTENT_URI.buildUpon()
58 | .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
59 | .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, name)
60 | .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL)
61 | .build()
62 | val resourceUri = ctx.contentResolver.insert(insertUri, value)
63 | return ContentUris.parseId(resourceUri) > 0
64 | } finally {
65 | val deleteUri = CalendarContract.Calendars.CONTENT_URI.buildUpon().build()
66 | ctx.contentResolver.delete(deleteUri, CalendarContract.Calendars.ACCOUNT_NAME + "=?", arrayOf(account))
67 | }
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/WriteCallLogCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.ContentResolver
20 | import android.content.ContentUris
21 | import android.content.ContentValues
22 | import android.content.Context
23 | import android.provider.CallLog
24 | import android.support.annotation.RequiresPermission
25 | import halo.android.permission.checker.StrictChecker
26 |
27 | /**
28 | * Created by Lucio on 18/4/24.
29 | * 检查 写 通话记录 权限
30 | * 备注:API16以下默认返回true
31 | */
32 | class WriteCallLogCheck(ctx: Context) : BaseCheck(ctx) {
33 |
34 | private val testNumber = "1"
35 | private val testDate = System.currentTimeMillis()
36 |
37 | @RequiresPermission(StrictChecker.WRITE_CALL_LOG)
38 | override fun check(): Boolean = tryCheck {
39 | val contentResolver: ContentResolver = ctx.contentResolver
40 | try {
41 | //插入测试数据
42 | val content = ContentValues()
43 | content.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE)
44 | content.put(CallLog.Calls.NUMBER, testNumber)
45 | content.put(CallLog.Calls.DATE, testDate)
46 | content.put(CallLog.Calls.NEW, "0")
47 | val resourceUri = contentResolver.insert(CallLog.Calls.CONTENT_URI, content)
48 | return ContentUris.parseId(resourceUri) > 0
49 | } finally {
50 | //删除测试数据
51 | contentResolver.delete(CallLog.Calls.CONTENT_URI, CallLog.Calls.NUMBER + "=?", arrayOf(testNumber))
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/WriteContactsCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.ContentUris
20 | import android.content.ContentValues
21 | import android.content.Context
22 | import android.provider.ContactsContract
23 |
24 |
25 | /**
26 | * Created by Lucio on 18/4/24.
27 | * 检查读取联系人权限
28 | */
29 | class WriteContactsCheck(ctx: Context) : BaseCheck(ctx) {
30 |
31 | override fun check(): Boolean = tryCheck {
32 | //在联系人表中插入一条数据,再删除数据,如果成功则认为有权限
33 | var rawContactId: Long = -1
34 | var dataId: Long = -1
35 | val contentResolver = ctx.contentResolver
36 | try {
37 | //在ContactsContract.RawContacts.CONTENT_URI中插入一条数据
38 | val values = ContentValues()
39 | val rawContractUri = contentResolver.insert(ContactsContract.RawContacts.CONTENT_URI, values)
40 | rawContactId = ContentUris.parseId(rawContractUri)
41 |
42 | //在ContactsContract.Data.CONTENT_URI中插入一条数据
43 | values.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
44 | values.put(ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId)
45 | values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "WriteContactsCheck");
46 | values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "1")
47 | val dataUri = contentResolver.insert(ContactsContract.Data.CONTENT_URI, values)
48 | dataId = ContentUris.parseId(dataUri)
49 |
50 | return true
51 | } finally {
52 | try {//预防脏数据
53 | //删除[ContactsContract.RawContacts.CONTENT_URI]中新增的数据
54 | if (rawContactId > 0) {
55 | contentResolver.delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.RawContacts._ID + "=?", arrayOf(rawContactId.toString()))
56 | }
57 | } catch (e: Exception) {
58 | }
59 |
60 | try {//预防脏数据
61 | //删除[ContactsContract.Data.CONTENT_URI]中新增的数据
62 | if (dataId > 0)
63 | contentResolver.delete(ContactsContract.Data.CONTENT_URI, ContactsContract.Data._ID + "=?", arrayOf(dataId.toString()))
64 | } catch (e: Exception) {
65 | }
66 | }
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/checker/strict/WriteExternalStorageCheck.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.checker.strict
18 |
19 | import android.content.Context
20 | import android.os.Environment
21 | import java.io.File
22 |
23 |
24 | /**
25 | * Created by Lucio on 18/4/24.
26 | * 检测外置存储卡是否可写
27 | */
28 |
29 | class WriteExternalStorageCheck(ctx: Context) : BaseCheck(ctx) {
30 |
31 | override fun check(): Boolean = tryCheck {
32 | if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED)
33 | // sd卡不存在
34 | return true
35 |
36 | val directory = Environment.getExternalStorageDirectory()
37 | if (!directory.exists() || !directory.canWrite())
38 | return false
39 |
40 | val testFile = File(directory, "permission_check.txt")
41 | if (testFile.exists()) {
42 | return testFile.delete()
43 | } else {
44 | return testFile.createNewFile()
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/common/IntentSupport.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.common
18 |
19 | import android.annotation.TargetApi
20 | import android.content.Context
21 | import android.content.Intent
22 | import android.net.Uri
23 | import android.os.Build
24 | import android.provider.Settings
25 |
26 | /**
27 | * Created by Lucio on 2019/6/25.
28 | */
29 |
30 | object IntentSupport {
31 |
32 | /**
33 | * 系统设置界面
34 | */
35 | @JvmStatic
36 | fun createSystemSettingIntent(): Intent {
37 | return Intent(Settings.ACTION_SETTINGS)
38 | }
39 |
40 | /**
41 | * 应用详情界面
42 | */
43 | @JvmStatic
44 | fun createAppDetailSettingIntent(pkgName: String): Intent {
45 | return Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
46 | data = Uri.fromParts("package", pkgName, null)
47 | }
48 | }
49 |
50 | @JvmStatic
51 | fun createAppDetailSettingIntent(ctx: Context): Intent {
52 | return createAppDetailSettingIntent(ctx.packageName)
53 | }
54 |
55 | /**
56 | * 所有应用详情界面
57 | */
58 | fun createAllAppSettingIntent(ctx: Context): Intent {
59 | return Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS)
60 | }
61 |
62 |
63 | /**
64 | * 创建系统通知设置意图
65 | */
66 | @JvmStatic
67 | fun createNotificationSettingIntent(ctx: Context): Intent {
68 | val sdkInt = Build.VERSION.SDK_INT
69 | return if (sdkInt >= 26) {
70 | createNotificationSettingApi26(ctx)
71 | } else if (sdkInt >= 19) {
72 | createNotificationSettingApi19(ctx)
73 | } else {
74 | createNotificationSettingDefault(ctx)
75 | }
76 | }
77 |
78 | @JvmStatic
79 | private fun createNotificationSettingDefault(ctx: Context): Intent {
80 | var intent = createAppDetailSettingIntent(ctx.packageName)
81 | if (intent.resolveActivity(ctx.packageManager) == null) {
82 | intent = createSystemSettingIntent()
83 | }
84 | return intent
85 | }
86 |
87 | @JvmStatic
88 | @TargetApi(26)
89 | private fun createNotificationSettingApi26(ctx: Context): Intent {
90 | return Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
91 | .apply {
92 | putExtra(Settings.EXTRA_APP_PACKAGE, ctx.packageName)
93 | putExtra(Settings.EXTRA_CHANNEL_ID, ctx.applicationInfo.uid)
94 | }
95 | }
96 |
97 | @JvmStatic
98 | @TargetApi(19)
99 | private fun createNotificationSettingApi19(ctx: Context): Intent {
100 | return Intent("android.settings.APP_NOTIFICATION_SETTINGS")
101 | .apply {
102 | putExtra("app_package", ctx.packageName)
103 | putExtra("app_uid", ctx.applicationInfo.uid)
104 | }
105 | }
106 |
107 | /**
108 | * 创建通知渠道设置意图
109 | */
110 | fun createNotificationChanelSettingIntent(ctx: Context,channelId:String):Intent{
111 | if(Build.VERSION.SDK_INT >= 26){
112 | return IntentSupport.createNotificationChanelSettingIntentApi26(ctx, channelId)
113 | }else{
114 | return createNotificationSettingIntent(ctx)
115 | }
116 | }
117 |
118 | /**
119 | * 创建通知渠道设置意图
120 | */
121 | @TargetApi(26)
122 | fun createNotificationChanelSettingIntentApi26(ctx: Context,channelId:String):Intent{
123 | val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
124 | intent.putExtra(Settings.EXTRA_APP_PACKAGE, ctx.packageName)
125 | intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
126 | return intent
127 | }
128 |
129 | /**
130 | * 意图:当前应用的"未知来源管理"
131 | */
132 | @JvmStatic
133 | @TargetApi(Util.O)
134 | fun createAppUnknownSourceManagerIntent(ctx: Context): Intent {
135 | // 开启当前应用的权限管理页
136 | val packageUri = Uri.fromParts("package", ctx.packageName, null)
137 | return Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageUri)
138 | }
139 |
140 | /**
141 | * 请求系统设置修改Intent
142 | */
143 | @JvmStatic
144 | @TargetApi(Util.M)
145 | fun createWriteSystemSettingIntent(ctx: Context): Intent {
146 | val packageURI = Uri.fromParts("package", ctx.packageName, null)
147 | return Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, packageURI)
148 | }
149 |
150 | /**
151 | * 请求设置悬浮窗权限
152 | */
153 | @JvmStatic
154 | @TargetApi(Util.M)
155 | fun createDrawOverlaysSettingIntent(ctx: Context): Intent {
156 | val packageURI = Uri.fromParts("package", ctx.packageName, null)
157 | return Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, packageURI)
158 | }
159 |
160 | }
161 |
162 | internal fun Intent.orAppSettingIntent(ctx: Context): Intent {
163 | return if (resolveActivity(ctx.packageManager) == null) {
164 | IntentSupport.createAppDetailSettingIntent(ctx)
165 | }else {
166 | this
167 | }
168 | }
169 |
170 | internal fun Intent.orSystemSettingIntent(ctx: Context):Intent{
171 | return if (resolveActivity(ctx.packageManager) == null) {
172 | IntentSupport.createSystemSettingIntent()
173 | }else {
174 | this
175 | }
176 | }
177 |
178 | internal fun Intent.orDefault(ctx: Context):Intent{
179 | return this.orAppSettingIntent(ctx)
180 | .orSystemSettingIntent(ctx)
181 | }
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/common/PLog.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.common
18 |
19 | import android.util.Log
20 |
21 | /**
22 | * Created by Lucio on 2019/6/23.
23 | */
24 | object PLog {
25 |
26 | const val TAG = "HaloPermission"
27 |
28 | private var isDebug: Boolean = true
29 |
30 | fun setDebug(isDebug: Boolean) {
31 | this.isDebug = isDebug
32 | }
33 |
34 | private inline fun debugRun(action: () -> Unit) {
35 | if (isDebug) {
36 | action()
37 | }
38 | }
39 |
40 | fun d(msg: String) {
41 | d(TAG, msg)
42 | }
43 |
44 | fun d(tag: String, msg: String) {
45 | debugRun {
46 | Log.d(tag, msg)
47 | }
48 | }
49 |
50 | fun i(msg: String) {
51 | i(TAG, msg)
52 | }
53 |
54 | fun i(tag: String, msg: String) {
55 | debugRun {
56 | Log.i(tag, msg)
57 | }
58 | }
59 |
60 | fun e(msg: String) {
61 | e(TAG, msg)
62 | }
63 |
64 | fun e(tag: String, msg: String) {
65 | debugRun {
66 | Log.e(tag, msg)
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/common/Util.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.common
18 |
19 | import android.app.Activity
20 | import android.content.Context
21 | import android.content.pm.PackageManager
22 | import android.os.Build
23 | import android.support.v4.app.ActivityCompat
24 | import android.support.v4.content.ContextCompat
25 | import android.support.v4.content.PermissionChecker
26 |
27 | /**
28 | * Created by Lucio on 18/4/8.
29 | */
30 |
31 | object Util {
32 |
33 | const val KITKAT = 19
34 |
35 | //23版本以下的没有[Build.VERSION_CODES.M]变量
36 | const val M = 23
37 |
38 | const val O = 26
39 |
40 | /**
41 | * 是否需要进行权限处理(目标版本是否是23及以上)
42 | */
43 | @JvmStatic
44 | fun isPermissionTargetVersion(ctx: Context): Boolean {
45 | val packInfo = ctx.packageManager.getPackageInfo(ctx.packageName, 0)
46 | return packInfo.applicationInfo.targetSdkVersion >= M
47 | }
48 |
49 | /**
50 | * 权限是否被允许
51 | * ps:因为方法可能被很多地方调用,因此不用内联,否则可能增加编译代码
52 | */
53 | @JvmStatic
54 | fun isPermissionGranted(ctx: Context, permission: String): Boolean {
55 | if (Build.VERSION.SDK_INT >= M) {
56 | // targetSdkVersion >= Android M, we can use Context#checkSelfPermission
57 | if (isPermissionTargetVersion(ctx)) {
58 | return ContextCompat.checkSelfPermission(ctx, permission) == PackageManager.PERMISSION_GRANTED
59 | } else {
60 | // targetSdkVersion < Android M, we have to use PermissionChecker
61 | return PermissionChecker.checkSelfPermission(ctx, permission) == PermissionChecker.PERMISSION_GRANTED
62 | }
63 | }
64 | // For Android < Android M, self permissions are always granted.
65 | return true
66 | }
67 |
68 | /**
69 | * [ps: 创建此方法主要是为了对Activity的shouldShowRequestPermissionRationale做更好的说明]
70 | * 如果app之前请求过该权限,被用户拒绝, 这个方法就会返回true.
71 | * 如果用户之前拒绝权限的时候勾选了对话框中”Don’t ask again”的选项,那么这个方法会返回false.
72 | * 如果设备策略禁止应用拥有这条权限, 这个方法也返回false.
73 | * @param permission 权限
74 | * @exception IllegalArgumentException 如果传递的permission是当前设备无法识别的权限则会抛出 java.lang.IllegalArgumentException: Unknown permission: [permission]
75 | */
76 | @JvmStatic
77 | @Throws(IllegalArgumentException::class)
78 | fun shouldShowRequestPermissionRationale(ctx: Context, permission: String): Boolean {
79 | if (ctx is Activity) {
80 | return ActivityCompat.shouldShowRequestPermissionRationale(ctx, permission)
81 | } else {
82 |
83 | val packageManager = ctx.packageManager
84 | val pkManagerClass = packageManager.javaClass
85 | try {
86 | val method = pkManagerClass.getMethod("shouldShowRequestPermissionRationale", String::class.java)
87 | if (!method.isAccessible) method.isAccessible = true
88 | return method.invoke(packageManager, permission) as Boolean
89 | } catch (e: Exception) {
90 | return false
91 | }
92 | }
93 | }
94 |
95 | @JvmStatic
96 | fun shouldShowRequestPermissionRationale(fragment: android.app.Fragment, permission: String): Boolean {
97 | if (Build.VERSION.SDK_INT >= M) {
98 | return fragment.shouldShowRequestPermissionRationale(permission)
99 | }
100 | return false
101 | }
102 |
103 | @JvmStatic
104 | fun shouldShowRequestPermissionRationale(fragment: android.support.v4.app.Fragment, permission: String): Boolean {
105 | if (Build.VERSION.SDK_INT >= M) {
106 | return fragment.shouldShowRequestPermissionRationale(permission)
107 | }
108 | return false
109 | }
110 |
111 | /**
112 | * Checks whether a particular permissions has been unable for a
113 | * package by policy. Typically the device owner or the profile owner
114 | * may apply such a policy. The user cannot grant policy unable
115 | * permissions, hence the only way for an app to get such a permission
116 | * is by a policy change.
117 | *
118 | * @return 权限是否被政策限制: Whether the permission is restricted by policy.
119 | */
120 | @JvmStatic
121 | @Deprecated(message = "不知道具体使用场景")
122 | fun isPermissionRevoked(ctx: Context, permission: String): Boolean {
123 | if (Build.VERSION.SDK_INT >= M) {
124 | return ctx.applicationContext.packageManager.isPermissionRevokedByPolicy(permission, ctx.packageName)
125 | }
126 | // For Android < Android M, 默认权限没有被政策限制
127 | return false
128 | }
129 |
130 | /**
131 | * 是否在清单文件中申明权限
132 | */
133 | fun isManifestPermission(ctx: Context, permission: String): Boolean {
134 | val permissions = ctx.applicationContext.packageManager.getPackageInfo(ctx.applicationContext.packageName, PackageManager.GET_PERMISSIONS)
135 | return permissions.requestedPermissions.contains(permission)
136 | }
137 |
138 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/processor/PermissionProcessor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.processor
18 |
19 | import android.content.Intent
20 | import halo.android.permission.caller.PermissionCaller
21 | import halo.android.permission.checker.PermissionChecker
22 | import halo.android.permission.common.PLog
23 | import halo.android.permission.request.PermissionRequest
24 | import halo.android.permission.request.RationaleRender
25 | import halo.android.permission.setting.PermissionSetting
26 | import halo.android.permission.setting.SettingRender
27 | import halo.android.permission.setting.SettingResponder
28 |
29 | /**
30 | * Created by Lucio on 2019/6/22.
31 | */
32 |
33 | class PermissionProcessor(val request: PermissionRequest,
34 | val caller: PermissionCaller,
35 | val checker: PermissionChecker) : PermissionResponder, SettingResponder {
36 |
37 | //被拒绝的权限集合
38 | private var permissionStates: List = listOf()
39 |
40 | /**
41 | * 控制SettingRender只显示一次
42 | */
43 | private var isSettingRenderDone = false
44 |
45 | /**
46 | * 控制在设置界面关闭之后,是否自动重新检查权限
47 | */
48 | private var autoCheckWhenSettingResult: Boolean = true
49 |
50 | /**
51 | * 校验请求
52 | */
53 | fun invoke() {
54 | val permissions = request.permissions
55 | PLog.d("[invoke]:即将请求以下权限:${permissions.joinToString(",")}")
56 | //过滤被拒绝的权限
57 | val denyPermissions = permissions.filter {
58 | !checker.isPermissionGranted(caller.ctx, it)
59 | }
60 | if (denyPermissions.isNotEmpty()) {
61 | PLog.d("[invoke]:以下权限未授权,进行请求${denyPermissions.joinToString(",")}")
62 | requestPermissionsReal()
63 | // if (Build.VERSION.SDK_INT >= Util.M) {//有未授权权限
64 | // //过滤Rationale权限
65 | // val rationalePermissions = permissionStates.filter {
66 | // checker.shouldShowRequestPermissionRationale(invokecaller.ctx, it)
67 | // }
68 | // if (!rationalePermissions.isNullOrEmpty()) {
69 | // //有Rationale权限,执行RatinaleRender流程,判断是否需要向用户解释获取权限的原因
70 | // invokeRationaleRenderProcess(rationalePermissions)
71 | // } else {
72 | // //没有Rationale权限,直接请求权限得到结果
73 | // requestPermissionsReal()
74 | // }
75 | // } else {
76 | // //23以下,直接处理SettingRender
77 | // invokeSettingRenderProcess()
78 | // }
79 | } else {
80 | PLog.d("[invoke]:所有权限已授权,直接通知成功回调")
81 | //所有权限已通过,直接触发回调
82 | notifyPermissionSucceed()
83 | }
84 | }
85 |
86 | /**
87 | * 权限请求成功
88 | */
89 | protected fun notifyPermissionSucceed() {
90 | request.getGrandAction()?.onPermissionGrand(request.permissions.toList())
91 | }
92 |
93 | private fun getDeniedPermissions():List{
94 | return permissionStates.filter{
95 | !it.isGrand
96 | }.map {
97 | it.value
98 | }
99 | }
100 | /**
101 | * 权限请求失败
102 | */
103 | protected fun notifyPermissionFailed() {
104 | request.getDenyAction()?.onPermissionDenied(getDeniedPermissions())
105 | }
106 |
107 | /**
108 | * 请求权限
109 | */
110 | private fun requestPermissionsReal() {
111 | caller.requestPermission(this, *request.permissions)
112 | }
113 |
114 | /**
115 | * [PermissionResponder]回调 处理请求的权限结果返回
116 | */
117 | override fun onPermissionResponderResult(permissions: Array, grantResults: IntArray) {
118 | PLog.d("[onPermissionResponderResult]")
119 | var containsNever: Boolean = false
120 | var exitsDenyPermission: Boolean = false
121 |
122 | val ctx = caller.ctx
123 | permissionStates = permissions.map { permission ->
124 | val isGrand = checker.isPermissionGranted(ctx, permission)
125 | val shouldRationale = checker.shouldShowRequestPermissionRationale(ctx, permission)
126 |
127 | if (!isGrand) {
128 | exitsDenyPermission = true
129 | }
130 |
131 | if (!isGrand && !shouldRationale) {
132 | //如果权限被拒绝,并不允许show rationale,则说明权限被拒绝并且被设置为不再询问等,
133 | // 也就是说即便二次请求也无法获得权限
134 | containsNever = true
135 | }
136 | PermissionState(permission, isGrand, shouldRationale)
137 | }
138 |
139 | if (exitsDenyPermission) {//存在未授权权限
140 | PLog.d("[onPermissionResponderResult]:存在未授权权限")
141 | if (containsNever) {//存在无法二次请求获取授权的权限
142 | PLog.d("[onPermissionResponderResult]:存在无法二次请求获取授权的权限,执行SettingRender")
143 | invokeSettingRenderProcess()
144 | } else {
145 | PLog.d("[onPermissionResponderResult]:执行RatinalRender")
146 | invokeRationaleRenderProcess(permissionStates.filter {
147 | it.shouldRationale
148 | }.map {
149 | it.value
150 | })
151 | }
152 | } else {
153 | PLog.d("[onPermissionResponderResult]:所有权限已授权,进行成功回调")
154 | notifyPermissionSucceed()
155 | }
156 |
157 | // permissionStates.clear()
158 | // //过滤被拒绝的权限
159 | // permissions.filterTo(permissionStates) {
160 | // !checker.isPermissionGranted(invokecaller.ctx, it)
161 | // }
162 | //
163 | // if (permissionStates.isNullOrEmpty()) {
164 | // notifyPermissionSucceed()
165 | // } else {
166 | // invokeSettingRenderProcess()
167 | // }
168 | }
169 |
170 | /**
171 | * 执行RationaleRender流程
172 | */
173 | private fun invokeRationaleRenderProcess(permissions: List) {
174 | PLog.d("[invokeRationaleRenderProcess]")
175 | val rationaleRender = request.getRationaleRender()
176 | if (rationaleRender != null) {
177 | PLog.d("[invokeRationaleRenderProcess]:执行RationaleRender展示")
178 | //执行RationaleRender显示
179 | rationaleRender.show(caller.ctx, permissions, mRationaleRenderProcess)
180 | } else {
181 | PLog.d("[invokeRationaleRenderProcess]:未设置RationaleRender,执行invokeSettingRenderProcess")
182 | invokeSettingRenderProcess()
183 | // //没有设置RationaleRender,则直接请求权限
184 | // requestPermissionsReal()
185 | }
186 | }
187 |
188 | /**
189 | * RationaleRender回调
190 | */
191 | private val mRationaleRenderProcess: RationaleRender.Process by lazy {
192 | object : RationaleRender.Process {
193 | /**
194 | * 执行权限请求
195 | */
196 | override fun onNext() {
197 | PLog.d("[RationaleRender.Process]:onNext")
198 | requestPermissionsReal()
199 | }
200 |
201 | /**
202 | * 取消则执行权限设置流程
203 | */
204 | override fun onCancel() {
205 | PLog.d("[RationaleRender.Process]:onCancel")
206 | invokeSettingRenderProcess()
207 | }
208 | }
209 | }
210 |
211 | /**
212 | * 处理权限设置SettingRende
213 | */
214 | private fun invokeSettingRenderProcess() {
215 | PLog.d("[invokeSettingRenderProcess]")
216 | val settingRender = request.getSettingRender()
217 | if (settingRender != null && !isSettingRenderDone) {
218 | PLog.d("[invokeSettingRenderProcess]:已设置SettingRender,并且未显示过权限设置界面,显示")
219 | isSettingRenderDone = true
220 | settingRender.show(caller.ctx, getDeniedPermissions(), mSettingRenderProcess)
221 | } else {
222 | PLog.d("[invokeSettingRenderProcess]:未设置SettingRender,通知权限请求失败")
223 | notifyPermissionFailed()
224 | }
225 | }
226 |
227 | /**
228 | * RationaleView回调
229 | */
230 | private val mSettingRenderProcess: SettingRender.Process by lazy {
231 |
232 | object : SettingRender.Process {
233 |
234 | /**
235 | * RationaleView回调 下一步
236 | */
237 | override fun onNext(autoCheckWhenSettingResult: Boolean) {
238 | PLog.d("[SettingRender.Process]:onNext(autoCheckWhenSettingResult=$autoCheckWhenSettingResult)")
239 | this@PermissionProcessor.autoCheckWhenSettingResult = autoCheckWhenSettingResult
240 | requestPermissionSettingReal()
241 | }
242 |
243 | /**
244 | * RationaleView回调 取消
245 | */
246 | override fun onCancel() {
247 | PLog.d("[SettingRender.Process]:onCancel")
248 | notifyPermissionFailed()
249 | }
250 |
251 | }
252 | }
253 |
254 | /**
255 | * 请求权限设置界面
256 | */
257 | private fun requestPermissionSettingReal() {
258 | val settingIntent = PermissionSetting.getCanResolvedSettingIntent(caller.ctx, request.getSettingRender()?.getCustomSettingIntent(caller.ctx))
259 | if (settingIntent != null) {
260 | caller.requestPermissionSetting(this, settingIntent)
261 | } else {
262 | notifySettingResult()
263 | }
264 | }
265 |
266 | /**
267 | * [SettingResponder]回调
268 | */
269 | override fun onSettingResponderResult(resultCode: Int, data: Intent?) {
270 | notifySettingResult()
271 | }
272 |
273 | private fun notifySettingResult() {
274 | if (autoCheckWhenSettingResult) {
275 | //过滤被拒绝的权限
276 | val deniedPermissions = request.permissions.filter{
277 | !checker.isPermissionGranted(caller.ctx, it)
278 | }
279 |
280 | if (deniedPermissions.isEmpty()) {
281 | notifyPermissionSucceed()
282 | } else {
283 | notifyPermissionFailed()
284 | }
285 | }
286 | }
287 |
288 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/processor/PermissionResponder.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * Created by Lucio on 18/4/6.
19 | */
20 | package halo.android.permission.processor
21 |
22 | /**
23 | * 权限请求结果回调
24 | * ps:用于接收向系统请求权限的结果回调
25 | */
26 | interface PermissionResponder {
27 |
28 | fun onPermissionResponderResult(permissions: Array, grantResults: IntArray)
29 |
30 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/processor/PermissionState.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.processor
18 |
19 | /**
20 | * Created by Lucio on 2019/6/23.
21 | */
22 |
23 | internal data class PermissionState(val value:String,val isGrand:Boolean,val shouldRationale:Boolean)
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/request/DefaultRender.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.request
18 |
19 | import android.content.Context
20 | import android.content.DialogInterface
21 | import android.support.v7.app.AlertDialog
22 | import halo.android.permission.setting.SettingRender
23 |
24 | /**
25 | * Created by Lucio on 2019/6/23.
26 | */
27 |
28 | abstract class DefaultAlertRender(val msg: String,
29 | val title: String? = null,
30 | val okText: String = "OK",
31 | val cancelText: String? = null) {
32 |
33 | fun buildAlert(ctx: Context): AlertDialog {
34 | val builder = AlertDialog.Builder(ctx).setMessage(msg)
35 | if (!title.isNullOrEmpty()) {
36 | builder.setTitle(title)
37 | }
38 |
39 | builder.setPositiveButton(okText, ::onPositiveButtonClick)
40 |
41 | if (!cancelText.isNullOrEmpty()) {
42 | builder.setNegativeButton(cancelText, ::onCancelButtonClick)
43 | }
44 |
45 | builder.setOnCancelListener(::onAlertCancelCallBack)
46 | return builder.create()
47 | }
48 |
49 | protected abstract fun onPositiveButtonClick(dialog: DialogInterface?, which: Int)
50 |
51 | protected abstract fun onCancelButtonClick(dialog: DialogInterface?, which: Int)
52 |
53 | protected abstract fun onAlertCancelCallBack(dialog: DialogInterface?)
54 |
55 | }
56 |
57 | /**
58 | * 默认的RationalRender
59 | */
60 | class DefaultRationaleRender(msg: String,
61 | title: String? = null,
62 | okText: String = "OK",
63 | cancelText: String? = null)
64 | : DefaultAlertRender(msg, title, okText, cancelText), RationaleRender {
65 |
66 | private var mProcess: RationaleRender.Process? = null
67 |
68 | override fun onPositiveButtonClick(dialog: DialogInterface?, which: Int) {
69 | mProcess?.onNext()
70 | mProcess = null
71 | }
72 |
73 | override fun onCancelButtonClick(dialog: DialogInterface?, which: Int) {
74 | mProcess?.onCancel()
75 | mProcess = null
76 | }
77 |
78 | override fun onAlertCancelCallBack(dialog: DialogInterface?) {
79 | mProcess?.onCancel()
80 | mProcess = null
81 | }
82 |
83 |
84 | override fun show(ctx: Context, permission: List, process: RationaleRender.Process) {
85 | mProcess = process
86 | buildAlert(ctx).show()
87 | }
88 |
89 | }
90 |
91 | /**
92 | * 默认的SettingRender实现
93 | */
94 | class DefaultSettingRender(msg: String,
95 | title: String? = null,
96 | okText: String = "OK",
97 | cancelText: String? = null)
98 | : DefaultAlertRender(msg, title, okText, cancelText), SettingRender {
99 |
100 | private var mProcess: SettingRender.Process? = null
101 |
102 | override fun onPositiveButtonClick(dialog: DialogInterface?, which: Int) {
103 | mProcess?.onNext()
104 | mProcess = null
105 | }
106 |
107 | override fun onCancelButtonClick(dialog: DialogInterface?, which: Int) {
108 | mProcess?.onCancel()
109 | mProcess = null
110 | }
111 |
112 | override fun onAlertCancelCallBack(dialog: DialogInterface?) {
113 | mProcess?.onCancel()
114 | mProcess = null
115 | }
116 |
117 | override fun show(ctx: Context, permission: List, process: SettingRender.Process) {
118 | mProcess = process
119 | buildAlert(ctx).show()
120 | }
121 |
122 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/request/PermissionRequest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.request
18 |
19 | import android.content.Context
20 | import android.support.v4.app.Fragment
21 | import android.support.v4.app.FragmentActivity
22 | import halo.android.permission.HaloPermission
23 | import halo.android.permission.caller.PermissionCaller
24 | import halo.android.permission.checker.PermissionChecker
25 | import halo.android.permission.processor.PermissionProcessor
26 | import halo.android.permission.setting.SettingRender
27 |
28 | /**
29 | * Created by Lucio on 2019/6/22.
30 | */
31 |
32 | class PermissionRequest(val permissions: Array) {
33 |
34 | /**
35 | * 用于渲染Rationale permission(通常是一个对话框提示用户让用户允许app接下来请求的权限)
36 | */
37 | private var mRationaleRender: RationaleRender? = null
38 |
39 | /**
40 | * 用于渲染请求设置权限(通常是一个对话框提示用户让用户设置应用权限)
41 | */
42 | private var mSettingRender: SettingRender? = null
43 |
44 | private var mGrandAction: GrandAction? = null
45 |
46 | private var mDenyAction: DenyAction? = null
47 |
48 |
49 | fun setRationaleRender(view: RationaleRender?): PermissionRequest {
50 | mRationaleRender = view
51 | return this
52 | }
53 |
54 | fun getRationaleRender(): RationaleRender? {
55 | return mRationaleRender
56 | }
57 |
58 | fun setSettingRender(view: SettingRender?): PermissionRequest {
59 | mSettingRender = view
60 | return this
61 | }
62 |
63 | fun getSettingRender(): SettingRender? {
64 | return mSettingRender
65 | }
66 |
67 | fun setGrandAction(action: GrandAction?): PermissionRequest {
68 | mGrandAction = action
69 | return this
70 | }
71 |
72 | fun getGrandAction(): GrandAction? {
73 | return mGrandAction
74 | }
75 |
76 | fun setDenyAction(action: DenyAction?): PermissionRequest {
77 | mDenyAction = action
78 | return this
79 | }
80 |
81 | fun getDenyAction(): DenyAction? {
82 | return mDenyAction
83 | }
84 |
85 | fun setListener(listener: PermissionListener?): PermissionRequest {
86 | mDenyAction = listener
87 | mGrandAction = listener
88 | return this
89 | }
90 |
91 | fun request(activity: FragmentActivity) {
92 | PermissionProcessor(this,
93 | HaloPermission.newFragmentCaller(activity),
94 | HaloPermission.newStandardChecker())
95 | .invoke()
96 | }
97 |
98 | fun request(fragment: Fragment) {
99 | PermissionProcessor(this,
100 | HaloPermission.newFragmentCaller(fragment),
101 | HaloPermission.newStandardChecker())
102 | .invoke()
103 | }
104 |
105 | /**
106 | * 执行请求
107 | */
108 | fun request(caller: PermissionCaller, checker: PermissionChecker) {
109 | PermissionProcessor(this, caller, checker)
110 | .invoke()
111 | }
112 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/request/RationaleRender.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.request
18 |
19 | import android.content.Context
20 |
21 | /**
22 | * Created by Lucio on 18/4/4.
23 | */
24 |
25 | interface RationaleRender {
26 |
27 | fun show(ctx: Context, permission: List, process: Process)
28 |
29 | interface Process {
30 | fun onNext()
31 |
32 | fun onCancel()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/request/RequestListener.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.request
18 |
19 | /**
20 | * Created by Lucio on 18/4/5.
21 | */
22 |
23 | interface DenyAction {
24 | fun onPermissionDenied(permissions: List)
25 | }
26 |
27 | interface GrandAction{
28 | fun onPermissionGrand(permissions: List)
29 | }
30 |
31 |
32 | interface PermissionListener :DenyAction,GrandAction
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/setting/PermissionSetting.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.setting
18 |
19 | import android.content.Context
20 | import android.content.Intent
21 | import android.net.Uri
22 | import android.os.Build
23 | import android.provider.Settings
24 | import android.util.Log
25 | import halo.android.permission.common.Util
26 | import java.io.BufferedReader
27 | import java.io.IOException
28 | import java.io.InputStreamReader
29 |
30 | /**
31 | * 各大手机厂商权限设置界面打开方法
32 | * 参考资料:https://www.jianshu.com/p/b5c494dba0bc
33 | * 暂未一一验证
34 | *
35 | * 关于如何知道系统设置界面的名字 可以通过' adb shell dumpsys activity activities '命令
36 | */
37 | object PermissionSetting {
38 |
39 | const val MF_HUAWEI = "huawei"
40 | const val MF_XIAOMI = "xiaomi"
41 | const val MF_OPPO = "oppo"
42 | const val MF_VIVO = "vivo"
43 | const val MF_SAMSUNG = "samsung"
44 | const val MF_MEIZU = "meizu"
45 | const val MF_SMARTISAN = "smartisan"
46 | const val MF_SONY = "sony"
47 | const val MF_LETV = "letv"
48 | const val MF_LG = "lg"
49 |
50 | fun obtainSettingIntent(ctx: Context): Intent {
51 | //获取设备制造商,缺陷:如果用于将手机刷机,比如使用华为的手机刷机小米的系统,这种情况得到的判断可能有误
52 | val mf = Build.MANUFACTURER.toLowerCase()
53 | if (mf.contains(MF_HUAWEI)) {
54 | return huawei(ctx)
55 | } else if (mf.contains(MF_XIAOMI)) {
56 | return xiaomi(ctx)
57 | } else if (mf.contains(MF_OPPO)) {
58 | return oppo(ctx)
59 | } else if (mf.contains(MF_VIVO)) {
60 | return vivo(ctx)
61 | } else if (mf.contains(MF_SAMSUNG)) {
62 | return samsung(ctx)
63 | } else if (mf.contains(MF_MEIZU)) {
64 | return meizu(ctx)
65 | } else if (mf.contains(MF_SMARTISAN)) {
66 | return smartisan(ctx)
67 | } else if (mf.contains(MF_SONY)) {
68 | return sony(ctx)
69 | } else if (mf.contains(MF_LETV)) {
70 | return letv(ctx)
71 | } else if (mf.contains(MF_LG)) {
72 | return lg(ctx)
73 | } else {
74 | return default(ctx)
75 | }
76 | }
77 |
78 |
79 | /**
80 | * 获取能够被正常处理的SettingIntent
81 | * @param customIntent 期望/自定义的权限设置界面
82 | * @return 校验之后可以被处理的的Intent
83 | */
84 | internal fun getCanResolvedSettingIntent(ctx: Context, customIntent: Intent? = null): Intent? {
85 | var settingIntent = customIntent
86 | if (settingIntent == null) {
87 | settingIntent = obtainSettingIntent(ctx)
88 | }
89 |
90 | if (settingIntent.resolveActivity(ctx.packageManager) != null)
91 | return settingIntent
92 |
93 | settingIntent = default(ctx)
94 |
95 | if (settingIntent.resolveActivity(ctx.packageManager) != null) {
96 | return settingIntent
97 | }
98 |
99 | settingIntent = Intent(Settings.ACTION_SETTINGS)
100 | if (settingIntent.resolveActivity(ctx.packageManager) != null) {
101 | return settingIntent
102 | }
103 |
104 | Log.e("SettingIntent", "no activity can handle intent for setting permission")
105 | return null
106 | }
107 |
108 | /**
109 | * 默认Intent
110 | */
111 | @JvmStatic
112 | fun default(context: Context): Intent {
113 | val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
114 | intent.data = Uri.fromParts("package", context.packageName, null)
115 | return intent
116 | }
117 |
118 | private fun huawei(ctx: Context): Intent {
119 | if (Build.VERSION.SDK_INT >= Util.M) {
120 | return default(ctx)
121 | }
122 | val intent = Intent()
123 | intent.putExtra("packageName", ctx.packageName)
124 | intent.setClassName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity")
125 | return intent
126 | }
127 |
128 |
129 | private fun xiaomi(ctx: Context): Intent {
130 | val miuiVersion = getMIUIVersion().toUpperCase()
131 | return when (miuiVersion) {
132 | "V6", "V7" -> {
133 | miuiV6_7(ctx)
134 | }
135 | "V8", "V9" -> {
136 | miuiV8_9(ctx)
137 | }
138 | else -> {
139 | default(ctx)
140 | }
141 | }
142 | }
143 |
144 | /**
145 | * 获取MIUI系统版本
146 | */
147 | private fun getMIUIVersion(): String {
148 | return getSystemProperty("ro.miui.ui.version.name") ?: ""
149 | }
150 |
151 | private fun miuiV6_7(ctx: Context): Intent {
152 | val intent = Intent("miui.intent.action.APP_PERM_EDITOR")
153 | intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
154 | intent.putExtra("extra_pkgname", ctx.packageName)
155 | return intent
156 | }
157 |
158 | private fun miuiV8_9(ctx: Context): Intent {
159 | val intent = Intent("miui.intent.action.APP_PERM_EDITOR")
160 | intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
161 | intent.putExtra("extra_pkgname", ctx.packageName)
162 | return intent
163 | }
164 |
165 | private fun vivo(ctx: Context): Intent {
166 | val intent = Intent()
167 | intent.putExtra("packagename", ctx.packageName)
168 | if (Build.VERSION.SDK_INT >= 25) {
169 | intent.setClassName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.SoftPermissionDetailActivity")
170 | } else {
171 | intent.setClassName("com.iqoo.secure", "com.iqoo.secure.MainActivity")
172 | }
173 | return intent
174 | }
175 |
176 | private fun oppo(ctx: Context): Intent {
177 | // val intent = Intent()
178 | // intent.putExtra("packageName", ctx.packageName)
179 | // intent.setClassName("com.coloros.safecenter", "com.coloros.safecenter.permission.PermissionManagerActivity");
180 | // return intent
181 | return default(ctx)
182 | }
183 |
184 | private fun meizu(context: Context): Intent {
185 | if (Build.VERSION.SDK_INT >= 25) {
186 | return default(context)
187 | }
188 |
189 | val intent = Intent("com.meizu.safe.security.SHOW_APPSEC")
190 | intent.putExtra("packageName", context.packageName)
191 | intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity")
192 | return intent
193 | }
194 |
195 | private fun smartisan(context: Context): Intent {
196 | return default(context)
197 | }
198 |
199 |
200 | private fun samsung(context: Context): Intent {
201 | return default(context)
202 | }
203 |
204 | /**
205 | * 索尼
206 | */
207 | private fun sony(ctx: Context): Intent {
208 | val intent = Intent()
209 | intent.putExtra("packageName", ctx.packageName)
210 | intent.setClassName("com.sonymobile.cta", "com.sonymobile.cta.SomcCTAMainActivity")
211 | return intent
212 | }
213 |
214 | /**
215 | * 乐视
216 | */
217 | private fun letv(ctx: Context): Intent {
218 | val intent = Intent()
219 | intent.putExtra("packageName", ctx.packageName)
220 | intent.setClassName("com.letv.android.letvsafe", "com.letv.android.letvsafe.PermissionAndApps")
221 | return intent
222 | }
223 |
224 | private fun lg(ctx: Context): Intent {
225 | val intent = Intent("android.intent.action.MAIN")
226 | intent.putExtra("packageName", ctx.packageName)
227 | intent.setClassName("com.android.settings", "com.android.settings.Settings'$'AccessLockSummaryActivity")
228 | return intent
229 | }
230 |
231 | private fun qihoo360(ctx: Context): Intent {
232 | val intent = Intent("android.intent.action.MAIN")
233 | intent.putExtra("packageName", ctx.packageName)
234 | intent.setClassName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity");
235 | return intent
236 | }
237 |
238 | /**
239 | * Returns a SystemProperty
240 | * @param propName The Property to retrieve
241 | * @return The Property, or NULL if not found
242 | * 此方法来源于[https://searchcode.com/codesearch/view/41537878/]
243 | */
244 | fun getSystemProperty(propName: String): String? {
245 | val line: String
246 | var input: BufferedReader? = null
247 | try {
248 | val p = Runtime.getRuntime().exec("getprop " + propName)
249 | input = BufferedReader(InputStreamReader(p.inputStream), 1024)
250 | line = input.readLine()
251 | input.close()
252 | } catch (ex: IOException) {
253 | Log.e("getSystemProperty", "Unable to read sysprop " + propName, ex)
254 | return null
255 | } finally {
256 | if (input != null) {
257 | try {
258 | input.close()
259 | } catch (e: IOException) {
260 | Log.e("getSystemProperty", "Exception while closing InputStream", e)
261 | }
262 | }
263 | }
264 | return line
265 | }
266 |
267 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/setting/SettingRender.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.setting
18 |
19 | import android.content.Context
20 | import android.content.DialogInterface
21 | import android.content.Intent
22 | import halo.android.permission.request.DefaultAlertRender
23 |
24 | /**
25 | * Created by Lucio on 18/4/4.
26 | */
27 | interface SettingRender {
28 |
29 | fun show(ctx: Context, permission: List, process: Process)
30 |
31 | /**
32 | * 获取自定义权限设置界面
33 | * ps:用于发起方自定义权限设置界面Intent而不使用HoloPermission提供的settingIntent,但是不建议这么做。
34 | * HoloPermission提供的权限设置界面会尽可能的兼容更多的设备
35 | */
36 | fun getCustomSettingIntent(ctx: Context): Intent? = null
37 |
38 | interface Process {
39 |
40 | /**
41 | * @param autoCheckWhenSettingResult 当进入权限设置界面之后返回,是否再自动检测权限
42 | */
43 | fun onNext(autoCheckWhenSettingResult: Boolean = true)
44 |
45 | fun onCancel()
46 | }
47 | }
48 |
49 | /**
50 | * 默认的SettingRender实现
51 | */
52 | internal class DefaultSettingRender(msg: String,
53 | title: String? = null,
54 | okText: String = "OK",
55 | cancelText: String? = null)
56 | : DefaultAlertRender(msg, title, okText, cancelText), SettingRender {
57 |
58 | private var mProcess: SettingRender.Process? = null
59 |
60 | override fun onPositiveButtonClick(dialog: DialogInterface?, which: Int) {
61 | mProcess?.onNext()
62 | mProcess = null
63 | }
64 |
65 | override fun onCancelButtonClick(dialog: DialogInterface?, which: Int) {
66 | mProcess?.onCancel()
67 | mProcess = null
68 | }
69 |
70 | override fun onAlertCancelCallBack(dialog: DialogInterface?) {
71 | mProcess?.onCancel()
72 | mProcess = null
73 | }
74 |
75 | override fun show(ctx: Context, permission: List, process: SettingRender.Process) {
76 | mProcess = process
77 | buildAlert(ctx).show()
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/setting/SettingResponder.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * Created by Lucio on 18/4/6.
19 | */
20 | package halo.android.permission.setting
21 |
22 | import android.content.Intent
23 |
24 | interface SettingResponder {
25 |
26 | fun onSettingResponderResult(resultCode: Int, data: Intent?)
27 |
28 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/spec/SpecPermission.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.spec
18 |
19 | import android.content.Context
20 | import android.content.Intent
21 |
22 | /**
23 | * Created by Lucio on 2019/6/27.
24 | */
25 |
26 | abstract class SpecPermission(val listener: SpecialListener) {
27 |
28 | abstract fun isGrand(ctx: Context): Boolean
29 |
30 | abstract fun isGrandOrThrow(ctx: Context): Boolean
31 |
32 | abstract fun createSettingIntent(ctx: Context): Intent
33 |
34 | fun notifyGrand() {
35 | listener.onSpecialGrand()
36 | }
37 |
38 | fun notifyDeny() {
39 | listener.onSpecialDeny()
40 | }
41 |
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/spec/SpecPermissionFragmentCaller.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.spec
18 |
19 | import android.content.Context
20 | import android.support.v4.app.Fragment
21 | import android.support.v4.app.FragmentActivity
22 | import android.support.v4.app.FragmentManager
23 | import halo.android.permission.caller.FragmentCaller
24 |
25 | /**
26 | * Created by Lucio on 2019/6/25.
27 | */
28 |
29 | class SpecPermissionFragmentCaller : FragmentCaller, SpecialCaller {
30 |
31 | constructor(ctx: Context, fm: FragmentManager) : super(ctx, fm)
32 | constructor(activity: FragmentActivity) : super(activity)
33 | constructor(fragment: Fragment) : super(fragment)
34 |
35 |
36 | override fun requestSpecialPermission(spec: SpecPermission) {
37 | callerFragment.requestSpecialPermission(spec)
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/spec/SpecPermissionImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.spec
18 |
19 | import android.content.Context
20 | import android.content.Intent
21 | import halo.android.permission.HaloSpecPermission
22 |
23 | /**
24 | * Created by Lucio on 2019/6/27.
25 | */
26 |
27 |
28 | //通知权限
29 | open class SpecPermissionNotification(listener: SpecialListener) : SpecPermission(listener) {
30 |
31 | override fun isGrandOrThrow(ctx: Context): Boolean {
32 | return isGrand(ctx)
33 | }
34 |
35 | override fun isGrand(ctx: Context): Boolean {
36 | return HaloSpecPermission.areNotificationEnabled(ctx)
37 | }
38 |
39 | override fun createSettingIntent(ctx: Context): Intent {
40 | return HaloSpecPermission.createNotificationSettingIntentOrDefault(ctx)
41 | }
42 | }
43 |
44 | //通知渠道权限
45 | class SpecPermissionNotificationChannel(listener: SpecialListener,val channelId:String)
46 | :SpecPermissionNotification(listener){
47 |
48 |
49 | override fun isGrand(ctx: Context): Boolean {
50 | //通知渠道的权限受限于通知总开关,所以需要先判断是否具有通知权限
51 | return super.isGrand(ctx) &&
52 | HaloSpecPermission.areNotificationChannelsEnabled(ctx,channelId)
53 | }
54 |
55 | override fun isGrandOrThrow(ctx: Context): Boolean {
56 | //通知渠道的权限受限于通知总开关,所以需要先判断是否具有通知权限
57 | return super.isGrandOrThrow(ctx) &&
58 | HaloSpecPermission.areNotificationChannelsEnabledOrThrow(ctx,channelId)
59 | }
60 |
61 | override fun createSettingIntent(ctx: Context): Intent {
62 | //没有通知权限时先打开通知开关界面,其次再打开通知渠道界面
63 | if(!super.isGrand(ctx)){
64 | return super.createSettingIntent(ctx)
65 | }else{
66 | return HaloSpecPermission.createNotificationChanelSettingIntentOrDefault(ctx, channelId)
67 | }
68 | }
69 |
70 | }
71 |
72 | //悬浮窗权限
73 | class SpecPermissionSystemAlertWindow(listener: SpecialListener) : SpecPermission(listener) {
74 |
75 | override fun isGrand(ctx: Context): Boolean {
76 | return HaloSpecPermission.areDrawOverlaysEnable(ctx)
77 | }
78 |
79 | override fun isGrandOrThrow(ctx: Context): Boolean {
80 | return HaloSpecPermission.areDrawOverlaysEnableOrThrow(ctx)
81 | }
82 |
83 | override fun createSettingIntent(ctx: Context): Intent {
84 | return HaloSpecPermission.createDrawOverlaysSettingIntentOrDefault(ctx)
85 | }
86 |
87 | }
88 |
89 | //未知来源安装权限
90 | class SpecPermissionPackageInstall(listener: SpecialListener) : SpecPermission(listener) {
91 |
92 | override fun isGrandOrThrow(ctx: Context): Boolean {
93 | return isGrand(ctx)
94 | }
95 |
96 | override fun isGrand(ctx: Context): Boolean {
97 | return HaloSpecPermission.areRequestPackageInstallsEnable(ctx)
98 | }
99 |
100 | override fun createSettingIntent(ctx: Context): Intent {
101 | return HaloSpecPermission.createAppUnknownSourceManagerIntentOrDefault(ctx)
102 | }
103 |
104 | }
105 |
106 | //系统设置修改
107 | class SpecPermissionWriteSystemSetting(listener: SpecialListener) : SpecPermission(listener) {
108 | override fun isGrandOrThrow(ctx: Context): Boolean {
109 | return HaloSpecPermission.areWriteSystemSettingEnableOrThrow(ctx)
110 | }
111 |
112 | override fun isGrand(ctx: Context): Boolean {
113 | return HaloSpecPermission.areWriteSystemSettingEnable(ctx)
114 | }
115 |
116 | override fun createSettingIntent(ctx: Context): Intent {
117 | return HaloSpecPermission.createWriteSystemSettingIntentOrDefault(ctx)
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/spec/SpecPermissionNotification.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.spec
18 |
19 | /**
20 | * Created by Lucio on 2019/6/27.
21 | */
22 |
23 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/spec/SpecType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.spec;
18 |
19 | import android.support.annotation.IntDef;
20 |
21 | import java.lang.annotation.Retention;
22 | import java.lang.annotation.RetentionPolicy;
23 |
24 | import static halo.android.permission.spec.SpecType.NOTIFICATION;
25 | import static halo.android.permission.spec.SpecType.NOTIFICATION_CHANNEL;
26 | import static halo.android.permission.spec.SpecType.SYSTEM_ALERT_WINDOW;
27 | import static halo.android.permission.spec.SpecType.UNKNOWN_APP_SOURCES;
28 | import static halo.android.permission.spec.SpecType.WRITE_SETTINGS;
29 |
30 | /**
31 | * Created by Lucio on 2019/6/27.
32 | * 特殊权限类型
33 | */
34 |
35 | @Retention(RetentionPolicy.SOURCE)
36 | @IntDef({NOTIFICATION, NOTIFICATION_CHANNEL, SYSTEM_ALERT_WINDOW, UNKNOWN_APP_SOURCES, WRITE_SETTINGS})
37 | @interface SpecType {
38 | /**
39 | * 通知权限
40 | */
41 | int NOTIFICATION = 1;
42 |
43 | /**
44 | * 通知渠道
45 | */
46 | int NOTIFICATION_CHANNEL = 2;
47 |
48 | /**
49 | * 悬浮窗权限
50 | */
51 | int SYSTEM_ALERT_WINDOW = 3;
52 |
53 | /**
54 | * 允许安装未知权限
55 | */
56 | int UNKNOWN_APP_SOURCES = 4;
57 |
58 | /**
59 | * 更改设置
60 | */
61 | int WRITE_SETTINGS = 5;
62 | }
63 |
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/spec/SpecialCaller.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.spec
18 |
19 | /**
20 | * Created by Lucio on 2019/6/24.
21 | */
22 |
23 | interface SpecialCaller {
24 |
25 | fun requestSpecialPermission(spec: SpecPermission)
26 |
27 | }
--------------------------------------------------------------------------------
/permission/src/main/java/halo/android/permission/spec/SpecialListener.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 Lucio
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package halo.android.permission.spec
18 |
19 | /**
20 | * Created by Lucio on 2019/6/24.
21 | */
22 |
23 | interface SpecialListener {
24 |
25 | fun onSpecialGrand()
26 |
27 | fun onSpecialDeny()
28 |
29 | /**
30 | * 默认不做任何操作,只继续后续流程
31 | */
32 | fun showRationalView(process: Process){
33 | process.onSpecialNext()
34 | }
35 |
36 |
37 | interface Process {
38 | fun onSpecialNext()
39 |
40 | fun onSpecialCancel()
41 | }
42 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':permission'
2 |
--------------------------------------------------------------------------------