diff --git a/README.md b/README.md
index 3c1b382..de271db 100644
--- a/README.md
+++ b/README.md
@@ -8,18 +8,40 @@
![](./images/g_permission_instruction.png)
+## Import
+第一步,在项目根目录下的`build.gradle`文件中引入`Jitpack`的库路径:
+```groovy
+allprojects {
+ repositories {
+ // Your codes ...
+ maven { url 'https://jitpack.io' }
+ }
+}
+```
+第二步,在需要依赖`GPermission`的`Module`下的`build.gradle`文件中添加依赖:
+```groovy
+dependencies {
+ // Your codes ...
+ // GPermission
+ implementation "com.github.ITGungnir:GPermission:$permission_version"
+}
+```
+
## Usage
第一步,在`AndroidManifest.xml`文件中添加要请求的权限:
```xml
+
+
```
第二步,在适当的地方请求权限:
```kotlin
+// with()方法中的参数可以是 FragmentActivity或Fragment的子类
GPermission.with(this)
// 请求权限成功时的回调
.onGranted {
@@ -31,9 +53,20 @@ GPermission.with(this)
// 开始请求权限
.request(
Manifest.permission.WRITE_EXTERNAL_STORAGE to "文件读写",
- Manifest.permission.READ_PHONE_STATE to "获取手机状态"
+ Manifest.permission.READ_PHONE_STATE to "获取手机状态",
+ Manifest.permission.CAMERA to "相机",
+ Manifest.permission.RECORD_AUDIO to "麦克风录音"
)
```
+`GPermission`还提供了验证权限是否已获取的方法:
+```kotlin
+tvResult.text = GPermission.with(this).allGranted(
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.CAMERA,
+ Manifest.permission.RECORD_AUDIO
+).toString()
+```
## License
```text
diff --git a/app/build.gradle b/app/build.gradle
index 5ce451f..57bc206 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -28,13 +28,8 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation "org.jetbrains.kotlin:kotlin-android-extensions-runtime:$kotlin_version"
- implementation "org.jetbrains.anko:anko-sdk25:$anko_version"
- implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
// Support
implementation "androidx.appcompat:appcompat:$appcompat_version"
- implementation "com.google.android.material:material:$material_version"
- implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
// Tools
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leak_canary_version"
debugImplementation "com.squareup.leakcanary:leakcanary-support-fragment:$leak_canary_version"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 35dbca7..801174c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,8 @@
+
+
-
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical">
+ android:textColor="@android:color/black" />
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/config.gradle b/config.gradle
index e847b6e..2f9b550 100644
--- a/config.gradle
+++ b/config.gradle
@@ -6,15 +6,11 @@ ext {
version_code = 1
version_name = '1.0.0'
// Kotlin
- kotlin_version = '1.3.31'
- anko_version = '0.10.8'
+ kotlin_version = '1.3.40'
// Support
appcompat_version = '1.0.2'
- material_version = '1.0.0'
- constraintlayout_version = '1.1.3'
// ReactiveX
rxjava_version = '2.2.4'
- rxpermissions_version = '0.10.2'
// Tools
jitpack_version = '2.1'
leak_canary_version = '1.6.3'
diff --git a/permission/build.gradle b/permission/build.gradle
index 1c63af7..ce2293e 100644
--- a/permission/build.gradle
+++ b/permission/build.gradle
@@ -1,8 +1,8 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
-apply plugin: 'com.github.dcendents.android-maven'
+apply plugin: 'com.github.dcendents.android-maven'
group = 'com.github.ITGungnir'
android {
@@ -24,14 +24,8 @@ android {
dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation "org.jetbrains.kotlin:kotlin-android-extensions-runtime:$kotlin_version"
- implementation "org.jetbrains.anko:anko-sdk25:$anko_version"
- implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
// Support
implementation "androidx.appcompat:appcompat:$appcompat_version"
- implementation "com.google.android.material:material:$material_version"
- implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
// ReactiveX
- api "io.reactivex.rxjava2:rxjava:$rxjava_version"
- implementation "com.github.tbruyelle:rxpermissions:$rxpermissions_version"
+ implementation "io.reactivex.rxjava2:rxjava:$rxjava_version"
}
diff --git a/permission/src/main/java/my/itgungnir/permission/GPermission.kt b/permission/src/main/java/my/itgungnir/permission/GPermission.kt
index e8bd376..73b33ff 100644
--- a/permission/src/main/java/my/itgungnir/permission/GPermission.kt
+++ b/permission/src/main/java/my/itgungnir/permission/GPermission.kt
@@ -6,22 +6,30 @@ import android.content.pm.PackageManager
import android.net.Uri
import android.provider.Settings
import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
-import com.tbruyelle.rxpermissions2.RxPermissions
+import androidx.lifecycle.ViewModelStoreOwner
class GPermission private constructor() {
private lateinit var context: FragmentActivity
- private lateinit var permissionUtil: RxPermissions
+ private lateinit var permissionUtil: GPermissionProxy
private var grantedCallback: (() -> Unit)? = null
private var deniedCallback: (() -> Unit)? = null
companion object {
- fun with(activity: FragmentActivity) = GPermission().apply {
- context = activity
- permissionUtil = RxPermissions(activity)
+ fun with(component: ViewModelStoreOwner) = GPermission().apply {
+ permissionUtil = GPermissionProxy.with(component)
+ context = when (component) {
+ is FragmentActivity ->
+ component
+ is Fragment ->
+ component.activity ?: throw IllegalArgumentException("Fragment didn't attach to any Activity.")
+ else ->
+ throw IllegalArgumentException("GPermission requested from wrong component.")
+ }
}
}
@@ -47,17 +55,19 @@ class GPermission private constructor() {
}
}
- private fun lackOnes(vararg permissions: Pair): List {
- return permissions.filter { granted(permission = it.first) }
+ fun allGranted(vararg permissions: String) = permissions.all { granted(it) }
+
+ private fun lackedOnes(vararg permissions: Pair): List {
+ return permissions.filter { !granted(permission = it.first) }
.map { it.second }
}
private fun granted(permission: String): Boolean =
- ContextCompat.checkSelfPermission(context.applicationContext, permission) == PackageManager.PERMISSION_DENIED
+ ContextCompat.checkSelfPermission(context.applicationContext, permission) == PackageManager.PERMISSION_GRANTED
private fun requestRepeatedly(vararg permissions: Pair) {
GPermissionDialog.Builder()
- .message("请允许系统获取${lackOnes(*permissions)}权限")
+ .message("请允许系统获取${lackedOnes(*permissions)}权限")
.onConfirm { request(*permissions) }
.onCancel { deniedCallback?.invoke() }
.create()
@@ -66,7 +76,7 @@ class GPermission private constructor() {
private fun requestManually(vararg permissions: Pair) {
GPermissionDialog.Builder()
- .message("由于系统无法获取${lackOnes(*permissions)}权限,不能正常运行,请开启权限后再使用!")
+ .message("由于系统无法获取${lackedOnes(*permissions)}权限,不能正常运行,请开启权限后再使用!")
.onConfirm { toSystemConfigPage() }
.onCancel { deniedCallback?.invoke() }
.create()
diff --git a/permission/src/main/java/my/itgungnir/permission/GPermissionDialog.kt b/permission/src/main/java/my/itgungnir/permission/GPermissionDialog.kt
index 32248c1..f07097e 100644
--- a/permission/src/main/java/my/itgungnir/permission/GPermissionDialog.kt
+++ b/permission/src/main/java/my/itgungnir/permission/GPermissionDialog.kt
@@ -10,7 +10,6 @@ import android.view.ViewGroup
import android.view.Window
import androidx.fragment.app.DialogFragment
import kotlinx.android.synthetic.main.dialog_g_permission.*
-import org.jetbrains.anko.backgroundDrawable
class GPermissionDialog private constructor() : DialogFragment() {
@@ -36,7 +35,7 @@ class GPermissionDialog private constructor() : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- view.backgroundDrawable = GradientDrawable().apply {
+ view.background = GradientDrawable().apply {
setColor(Color.WHITE)
cornerRadius = 10F
}
diff --git a/permission/src/main/java/my/itgungnir/permission/GPermissionFragment.kt b/permission/src/main/java/my/itgungnir/permission/GPermissionFragment.kt
new file mode 100644
index 0000000..726c1ee
--- /dev/null
+++ b/permission/src/main/java/my/itgungnir/permission/GPermissionFragment.kt
@@ -0,0 +1,69 @@
+package my.itgungnir.permission
+
+import android.annotation.TargetApi
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import io.reactivex.subjects.PublishSubject
+
+class GPermissionFragment private constructor() : Fragment() {
+
+ private val subjects = mutableMapOf>()
+
+ companion object {
+ const val PERMISSION_REQUEST_CODE = 0x1F
+
+ fun getInstance(manager: FragmentManager): GPermissionFragment =
+ (manager.findFragmentByTag(GPermissionFragment::class.java.name) ?: GPermissionFragment().apply {
+ manager.beginTransaction().add(this, GPermissionFragment::class.java.name).commitNow()
+ }) as GPermissionFragment
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ retainInstance = true
+ }
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ if (PERMISSION_REQUEST_CODE != requestCode) {
+ return
+ }
+ for (i in 0 until permissions.size) {
+ val subject = subjects[permissions[i]] ?: return
+ subjects.remove(permissions[i])
+ val granted = grantResults[i] == PackageManager.PERMISSION_GRANTED
+ subject.onNext(
+ Permission(
+ name = permissions[i],
+ granted = granted,
+ shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale(permissions[i])
+ )
+ )
+ subject.onComplete()
+ }
+ }
+
+ fun requestPermissions(permissions: Array) =
+ requestPermissions(permissions, PERMISSION_REQUEST_CODE)
+
+ fun getPermissionSubject(permission: String) =
+ subjects[permission]
+
+ fun setPermissionSubject(permission: String, subject: PublishSubject) {
+ subjects[permission] = subject
+ }
+
+ fun isPermissionSubjectExist(permission: String) =
+ subjects.containsKey(permission)
+
+ @TargetApi(Build.VERSION_CODES.M)
+ fun isPermissionGranted(permission: String): Boolean =
+ activity?.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
+
+ @TargetApi(Build.VERSION_CODES.M)
+ fun isPermissionRevoked(permission: String): Boolean =
+ activity?.packageManager?.isPermissionRevokedByPolicy(permission, activity!!.packageName) ?: false
+}
\ No newline at end of file
diff --git a/permission/src/main/java/my/itgungnir/permission/GPermissionProxy.kt b/permission/src/main/java/my/itgungnir/permission/GPermissionProxy.kt
new file mode 100644
index 0000000..fffc3ac
--- /dev/null
+++ b/permission/src/main/java/my/itgungnir/permission/GPermissionProxy.kt
@@ -0,0 +1,103 @@
+package my.itgungnir.permission
+
+import android.os.Build
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.ViewModelStoreOwner
+import io.reactivex.Observable
+import io.reactivex.ObservableTransformer
+import io.reactivex.subjects.PublishSubject
+import java.util.*
+
+class GPermissionProxy {
+
+ private lateinit var fragment: GPermissionFragment
+
+ private val TRIGGER = Unit
+
+ companion object {
+ fun with(component: ViewModelStoreOwner) = GPermissionProxy().apply {
+ fragment = when (component) {
+ is FragmentActivity -> GPermissionFragment.getInstance(component.supportFragmentManager)
+ is Fragment -> GPermissionFragment.getInstance(component.childFragmentManager)
+ else -> throw IllegalArgumentException("GPermission requested from wrong component.")
+ }
+ }
+ }
+
+ fun requestEachCombined(vararg permissions: String): Observable =
+ Observable.just(TRIGGER).compose(ensureEachCombined(*permissions))
+
+ private fun ensureEachCombined(vararg permissions: String): ObservableTransformer {
+ return ObservableTransformer { o ->
+ request(o, *permissions)
+ .buffer(permissions.size)
+ .flatMap { permissions ->
+ if (permissions.isEmpty()) {
+ Observable.empty()
+ } else {
+ val combineName = permissions.map { it.name }.reduce { acc, s -> "$acc, $s" }
+ val combineGranted = permissions.all { it.granted }
+ val combineRationale = permissions.any { it.shouldShowRequestPermissionRationale }
+ Observable.just(Permission(combineName, combineGranted, combineRationale))
+ }
+ }
+ }
+ }
+
+ private fun request(trigger: Observable<*>, vararg permissions: String): Observable {
+ if (permissions.isEmpty()) {
+ throw IllegalArgumentException("RxPermissions.request/requestEach requires at least one input permission")
+ }
+ return oneOf(trigger, pending(*permissions))
+ .flatMap { requestImplementation(*permissions) }
+ }
+
+ private fun oneOf(trigger: Observable<*>?, pending: Observable<*>): Observable<*> {
+ return if (trigger == null) {
+ Observable.just(TRIGGER)
+ } else Observable.merge(trigger, pending)
+ }
+
+ private fun pending(vararg permissions: String): Observable<*> {
+ for (p in permissions) {
+ if (!fragment.isPermissionSubjectExist(p)) {
+ return Observable.empty()
+ }
+ }
+ return Observable.just(TRIGGER)
+ }
+
+ private fun requestImplementation(vararg permissions: String): Observable {
+ val list = ArrayList>(permissions.size)
+ val unrequestedPermissions = ArrayList()
+ for (permission in permissions) {
+ if (isGranted(permission)) {
+ list.add(Observable.just(Permission(permission, true, false)))
+ continue
+ }
+ if (isRevoked(permission)) {
+ list.add(Observable.just(Permission(permission, false, false)))
+ continue
+ }
+ var subject = fragment.getPermissionSubject(permission)
+ if (subject == null) {
+ unrequestedPermissions.add(permission)
+ subject = PublishSubject.create()
+ fragment.setPermissionSubject(permission, subject)
+ }
+ list.add(subject)
+ }
+ if (unrequestedPermissions.isNotEmpty()) {
+ val unrequestedPermissionsArray = unrequestedPermissions.toTypedArray()
+ fragment.requestPermissions(unrequestedPermissionsArray)
+ }
+ return Observable.concat(Observable.fromIterable(list))
+ }
+
+ private fun isGranted(permission: String) =
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.M || fragment.isPermissionGranted(permission)
+
+ private fun isRevoked(permission: String) =
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.M || fragment.isPermissionRevoked(permission)
+}
\ No newline at end of file
diff --git a/permission/src/main/java/my/itgungnir/permission/Permission.kt b/permission/src/main/java/my/itgungnir/permission/Permission.kt
new file mode 100644
index 0000000..0ffb036
--- /dev/null
+++ b/permission/src/main/java/my/itgungnir/permission/Permission.kt
@@ -0,0 +1,7 @@
+package my.itgungnir.permission
+
+data class Permission(
+ val name: String,
+ val granted: Boolean,
+ val shouldShowRequestPermissionRationale: Boolean
+)
\ No newline at end of file
diff --git a/permission/src/main/res/layout/dialog_g_permission.xml b/permission/src/main/res/layout/dialog_g_permission.xml
index b4cf773..35420f3 100644
--- a/permission/src/main/res/layout/dialog_g_permission.xml
+++ b/permission/src/main/res/layout/dialog_g_permission.xml
@@ -1,67 +1,58 @@
-
+ android:textSize="14sp" />
+ android:background="#CCCCCC" />
-
+
-
+
-
-
\ No newline at end of file
+
+
+
+
+
+
\ No newline at end of file