Skip to content

Commit 8ca645e

Browse files
authored
Merge pull request #82 from PatilShreyas/2.0.0-dev
Release v2.0.0
2 parents a955beb + 01071f7 commit 8ca645e

File tree

20 files changed

+734
-416
lines changed

20 files changed

+734
-416
lines changed

README.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,22 @@ val permissionFlow = PermissionFlow.getInstance()
5353

5454
// Observe state of single permission
5555
suspend fun observePermission() {
56-
permissionFlow.getPermissionState(android.Manifest.permission.READ_CONTACTS).collect { state ->
56+
permissionFlow.getPermissionState(Manifest.permission.READ_CONTACTS).collect { state ->
5757
if (state.isGranted) {
58-
// Do something
58+
// Permission granted, access contacts.
59+
} else if (state.isRationaleRequired == true) {
60+
// Permission denied, but can be requested again
61+
} else {
62+
// Permission denied, and can't be requested again
5963
}
6064
}
6165
}
6266

6367
// Observe state of multiple permissions
6468
suspend fun observeMultiplePermissions() {
6569
permissionFlow.getMultiplePermissionState(
66-
android.Manifest.permission.READ_CONTACTS,
67-
android.Manifest.permission.READ_SMS
70+
Manifest.permission.READ_CONTACTS,
71+
Manifest.permission.READ_SMS
6872
).collect { state ->
6973
// All permission states
7074
val allPermissions = state.permissions
@@ -77,6 +81,9 @@ suspend fun observeMultiplePermissions() {
7781

7882
// List of denied permissions
7983
val deniedPermissions = state.deniedPermissions
84+
85+
// List of permissions requiring rationale
86+
val permissionsRequiringRationale = state.permissionsRequiringRationale
8087
}
8188
}
8289
```
@@ -90,9 +97,11 @@ State of a permission and state of multiple permissions can also be observed in
9097
fun ExampleSinglePermission() {
9198
val state by rememberPermissionState(Manifest.permission.CAMERA)
9299
if (state.isGranted) {
93-
// Render something
100+
// Permission granted
101+
} else if (state.isRationaleRequired == true) {
102+
// Permission denied, but can be requested again
94103
} else {
95-
// Render something else
104+
// Permission denied, and can't be requested again
96105
}
97106
}
98107

@@ -113,6 +122,9 @@ fun ExampleMultiplePermission() {
113122

114123
val deniedPermissions = state.deniedPermissions
115124
// Do something with `deniedPermissions`
125+
126+
val permissionsRequiringRationale = state.permissionsRequiringRationale
127+
// Do something with `permissionsRequiringRationale`
116128
}
117129
```
118130

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ buildscript {
1414
robolectricVersion = "4.11.1"
1515
mockkVersion = '1.13.10'
1616
turbineVersion = '1.1.0'
17+
androidxCoreTestingVersion = '1.5.0'
1718
spotlessVersion = '6.25.0'
1819
koverVersion = "0.5.1"
1920
startupVersion = "1.1.1"

permission-flow-compose/src/main/java/dev/shreyaspatil/permissionflow/compose/PermissionFlow.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ import dev.shreyaspatil.permissionFlow.contract.RequestPermissionsContract
3737
* if (state.isGranted) {
3838
* // Render something
3939
* } else {
40+
* if (state.isRationaleRequired) {
41+
* // Show rationale
42+
* }
4043
* // Render something else
4144
* }
4245
* }

permission-flow/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ dependencies {
5757

5858
// Testing
5959
testImplementation "junit:junit:$jUnitVersion"
60+
testImplementation "androidx.test:core:$androidxCoreTestingVersion"
6061
testImplementation("org.robolectric:robolectric:$robolectricVersion")
6162
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"
6263
testImplementation "app.cash.turbine:turbine:$turbineVersion"

permission-flow/src/main/java/dev/shreyaspatil/permissionFlow/PermissionFlow.kt

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import android.content.Context
1919
import dev.shreyaspatil.permissionFlow.PermissionFlow.Companion.getInstance
2020
import dev.shreyaspatil.permissionFlow.PermissionFlow.Companion.init
2121
import dev.shreyaspatil.permissionFlow.impl.PermissionFlowImpl
22-
import java.util.concurrent.Executors
2322
import kotlinx.coroutines.CoroutineDispatcher
24-
import kotlinx.coroutines.asCoroutineDispatcher
23+
import kotlinx.coroutines.DelicateCoroutinesApi
2524
import kotlinx.coroutines.flow.StateFlow
25+
import kotlinx.coroutines.newFixedThreadPoolContext
2626

2727
/**
2828
* A utility class which provides a functionality for observing state of a permission (whether it's
@@ -58,6 +58,10 @@ import kotlinx.coroutines.flow.StateFlow
5858
* .collect { state ->
5959
* if (state.isGranted) {
6060
* // Do something
61+
* } else {
62+
* if (state.isRationaleRequired) {
63+
* // Do something
64+
* }
6165
* }
6266
* }
6367
* }
@@ -93,6 +97,10 @@ interface PermissionFlow {
9397
* .collect { state ->
9498
* if (state.isGranted) {
9599
* // Do something
100+
* } else {
101+
* if (state.isRationaleRequired) {
102+
* // Do something
103+
* }
96104
* }
97105
* }
98106
* ```
@@ -160,9 +168,11 @@ interface PermissionFlow {
160168
/**
161169
* Starts listening the changes of state of permissions.
162170
*
163-
* Ideally it automatically starts listening lazily when [getMultiplePermissionState] method is
164-
* used for the first time. But this can be used to start to listen again after stopping
165-
* listening with [stopListening].
171+
* Ideally it automatically starts listening eagerly when application is started and created via
172+
* [dev.shreyaspatil.permissionFlow.initializer.PermissionFlowInitializer]. If initializer is
173+
* disabled, then starts listening lazily when [getPermissionState] [getPermissionEvent] or
174+
* [getMultiplePermissionState] method is used for the first time. But this can be used to start
175+
* to listen again after stopping listening with [stopListening].
166176
*/
167177
fun startListening()
168178

@@ -178,7 +188,8 @@ interface PermissionFlow {
178188
* getting instance.
179189
*/
180190
companion object {
181-
private val DEFAULT_DISPATCHER = Executors.newFixedThreadPool(2).asCoroutineDispatcher()
191+
@OptIn(DelicateCoroutinesApi::class)
192+
private val DEFAULT_DISPATCHER = newFixedThreadPoolContext(2, "PermissionFlow")
182193

183194
/**
184195
* Initializes this [PermissionFlow] instance with specified arguments.
@@ -189,10 +200,7 @@ interface PermissionFlow {
189200
*/
190201
@JvmStatic
191202
@JvmOverloads
192-
fun init(
193-
context: Context,
194-
dispatcher: CoroutineDispatcher = DEFAULT_DISPATCHER,
195-
) {
203+
fun init(context: Context, dispatcher: CoroutineDispatcher = DEFAULT_DISPATCHER) {
196204
PermissionFlowImpl.init(context, dispatcher)
197205
}
198206

permission-flow/src/main/java/dev/shreyaspatil/permissionFlow/State.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,13 @@ package dev.shreyaspatil.permissionFlow
2020
*
2121
* @property permission Name of a permission
2222
* @property isGranted State of a permission whether it's granted or not
23+
* @property isRationaleRequired Whether to show rationale for a permission or not.
2324
*/
24-
data class PermissionState(val permission: String, val isGranted: Boolean)
25+
data class PermissionState(
26+
val permission: String,
27+
val isGranted: Boolean,
28+
val isRationaleRequired: Boolean?,
29+
)
2530

2631
/**
2732
* State model for multiple permissions
@@ -37,4 +42,9 @@ data class MultiplePermissionState(val permissions: List<PermissionState>) {
3742

3843
/** List of permissions which are denied / not granted by user */
3944
val deniedPermissions by lazy { permissions.filter { !it.isGranted }.map { it.permission } }
45+
46+
/** List of permissions which are required to show rationale */
47+
val permissionsRequiringRationale by lazy {
48+
permissions.filter { it.isRationaleRequired == true }.map { it.permission }
49+
}
4050
}

permission-flow/src/main/java/dev/shreyaspatil/permissionFlow/contract/RequestPermissionsContract.kt

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,14 @@ class RequestPermissionsContract(
3535
private val contract: RequestMultiplePermissions = RequestMultiplePermissions(),
3636
private val permissionFlow: PermissionFlow = PermissionFlow.getInstance(),
3737
) : ActivityResultContract<Array<String>, Map<String, Boolean>>() {
38-
override fun createIntent(
39-
context: Context,
40-
input: Array<String>,
41-
): Intent {
42-
return contract.createIntent(context, input ?: emptyArray())
38+
39+
override fun createIntent(context: Context, input: Array<String>): Intent {
40+
return contract.createIntent(context, input)
4341
}
4442

45-
override fun parseResult(
46-
resultCode: Int,
47-
intent: Intent?,
48-
): Map<String, Boolean> {
43+
override fun parseResult(resultCode: Int, intent: Intent?): Map<String, Boolean> {
4944
return contract.parseResult(resultCode, intent).also {
50-
val permissions = it.keys.filterNotNull().toTypedArray()
45+
val permissions = it.keys.toList().toTypedArray()
5146
permissionFlow.notifyPermissionsChanged(*permissions)
5247
}
5348
}

permission-flow/src/main/java/dev/shreyaspatil/permissionFlow/impl/PermissionFlowImpl.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import androidx.annotation.VisibleForTesting
2121
import dev.shreyaspatil.permissionFlow.MultiplePermissionState
2222
import dev.shreyaspatil.permissionFlow.PermissionFlow
2323
import dev.shreyaspatil.permissionFlow.PermissionState
24+
import dev.shreyaspatil.permissionFlow.internal.ApplicationStateMonitor
2425
import dev.shreyaspatil.permissionFlow.watchmen.PermissionWatchmen
2526
import kotlinx.coroutines.CoroutineDispatcher
2627
import kotlinx.coroutines.flow.StateFlow
@@ -33,13 +34,13 @@ constructor(
3334
) : PermissionFlow {
3435

3536
override fun getPermissionState(permission: String): StateFlow<PermissionState> {
36-
return watchmen.watch(permission)
37+
return watchmen.watchState(permission)
3738
}
3839

3940
override fun getMultiplePermissionState(
4041
vararg permissions: String
4142
): StateFlow<MultiplePermissionState> {
42-
return watchmen.watchMultiple(permissions.toList().toTypedArray())
43+
return watchmen.watchMultipleState(permissions.toList().toTypedArray())
4344
}
4445

4546
override fun notifyPermissionsChanged(vararg permissions: String) {
@@ -62,9 +63,10 @@ constructor(
6263
@Synchronized
6364
fun init(context: Context, dispatcher: CoroutineDispatcher) {
6465
if (instance == null) {
66+
val monitor = ApplicationStateMonitor(context.applicationContext as Application)
6567
val watchmen =
6668
PermissionWatchmen(
67-
application = context.applicationContext as Application,
69+
appStateMonitor = monitor,
6870
dispatcher = dispatcher,
6971
)
7072
instance = PermissionFlowImpl(watchmen)

permission-flow/src/main/java/dev/shreyaspatil/permissionFlow/initializer/PermissionFlowInitializer.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import dev.shreyaspatil.permissionFlow.PermissionFlow
2323
class PermissionFlowInitializer : Initializer<Unit> {
2424
override fun create(context: Context) {
2525
PermissionFlow.init(context)
26+
PermissionFlow.getInstance().startListening()
2627
}
2728

2829
override fun dependencies(): List<Class<out Initializer<*>>> {

0 commit comments

Comments
 (0)