Skip to content

Commit

Permalink
Merge pull request #45 from PatilShreyas/v1.1.0
Browse files Browse the repository at this point in the history
Release v1.1.0
  • Loading branch information
PatilShreyas authored Apr 9, 2023
2 parents 01e3a52 + b04fb30 commit 43fe445
Show file tree
Hide file tree
Showing 30 changed files with 235 additions and 129 deletions.
9 changes: 2 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,8 @@ jobs:

- name: Publish Library
run: |
echo "Publishing library🚀"
./gradlew publish --no-daemon --no-parallel
echo "Published✅"
echo "Releasing repository...🚀"
./gradlew closeAndReleaseRepository
echo "Released✅"
echo "Publishing library 🚀"
./gradlew publishAllPublicationsToMavenCentral --no-configuration-cache
env:
ORG_GRADLE_PROJECT_VERSION_NAME: ${{ github.event.inputs.versionName }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/.idea/
.DS_Store
/build
*/build/
/captures
.externalNativeBuild
.cxx
Expand Down
104 changes: 63 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Permission Flow for Android

Know about real-time state of a Android app Permissions with Kotlin Flow APIs. _Made with ❤️ for
Know about real-time state of a Android app Permissions with Kotlin Flow APIs. _Made with ❤️ for
Android Developers_.

[![Build](https://github.com/PatilShreyas/permission-flow-android/actions/workflows/build.yml/badge.svg)](https://github.com/PatilShreyas/permission-flow-android/actions/workflows/build.yml)
Expand All @@ -20,13 +20,13 @@ Android Developers_.

## 💡Introduction

In big projects, app is generally divided in several modules and in such cases, if any individual
module is just a data module (_not having UI_) and need to know state of a permission, it's not
that easy. This library provides a way to know state of a permission throughout the app and
from any layer of the application safely.
In big projects, app is generally divided in several modules and in such cases, if any individual
module is just a data module (_not having UI_) and need to know state of a permission, it's not
that easy. This library provides a way to know state of a permission throughout the app and
from any layer of the application safely.

_For example, you can listen for state of contacts permission in class where you'll instantly show
list of contacts when permission is granted._
list of contacts when permission is granted._

It's a simple and easy to use library. Just Plug and Play.

Expand All @@ -49,23 +49,9 @@ dependencies {

_You can find latest version and changelogs in the [releases](https://github.com/PatilShreyas/permission-flow-android/releases)_.

### 2. Initialization
### 2. Observing a Permission State

Before using any API of the library, it needs to be initialized before.
Initialize **PermissionFlow** as follows (For example, in `Application` class):

```kotlin
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
PermissionFlow.init(this)
}
}
```

### 3. Observing a Permission State

#### 3.1 Observing Permission with `StateFlow`
#### 2.1 Observing Permission with `StateFlow`
A permission state can be subscribed by retrieving `StateFlow<PermissionState>` or `StateFlow<MultiplePermissionState>` as follows:

```kotlin
Expand Down Expand Up @@ -101,7 +87,7 @@ suspend fun observeMultiplePermissions() {
}
```

#### 3.2 Observing permissions in Jetpack Compose
#### 2.2 Observing permissions in Jetpack Compose

State of a permission and state of multiple permissions can also be observed in Jetpack Compose application as follows:

Expand All @@ -119,29 +105,29 @@ fun ExampleSinglePermission() {
@Composable
fun ExampleMultiplePermission() {
val state by rememberMultiplePermissionState(
Manifest.permission.CAMERA
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.READ_CONTACTS
)

if (state.allGranted) {
// Render something
}

val grantedPermissions = state.grantedPermissions
// Do something with `grantedPermissions`

val deniedPermissions = state.deniedPermissions
// Do something with `deniedPermissions`
}
```

### 4. Requesting permission with PermissionFlow
### 3. Requesting permission with PermissionFlow

It's necessary to use utilities provided by this library to request permissions so that whenever permission state
It's necessary to use utilities provided by this library to request permissions so that whenever permission state
changes, this library takes care of notifying respective flows.

#### 4.1 Request permission from Activity / Fragment
#### 3.1 Request permission from Activity / Fragment

Use [`registerForPermissionFlowRequestsResult()`](https://patilshreyas.github.io/permission-flow-android/docs/permission-flow/dev.shreyaspatil.permissionFlow.utils/register-for-permission-flow-requests-result.html) method to get `ActivityResultLauncher`
and use `launch()` method to request for permission.
Expand All @@ -157,7 +143,7 @@ class ContactsActivity : AppCompatActivity() {
}
```

#### 4.2 Request permission in Jetpack Compose
#### 3.2 Request permission in Jetpack Compose

Use [`rememberPermissionFlowRequestLauncher()`](https://patilshreyas.github.io/permission-flow-android/docs/permission-flow-compose/dev.shreyaspatil.permissionflow.compose/remember-permission-flow-request-launcher.html) method to get `ManagedActivityResultLauncher`
and use `launch()` method to request for permission.
Expand All @@ -166,17 +152,17 @@ and use `launch()` method to request for permission.
@Composable
fun Example() {
val permissionLauncher = rememberPermissionFlowRequestLauncher()

Button(onClick = { permissionLauncher.launch(android.Manifest.permission.CAMERA, ...) }) {
Text("Request Permissions")
}
}
}
```

### 5. Manually notifying permission state changes ⚠️
### 4. Manually notifying permission state changes ⚠️

If you're not using `ActivityResultLauncher` APIs provided by this library then
you will ***not receive permission state change updates***. But there's a provision by which
If you're not using `ActivityResultLauncher` APIs provided by this library then
you will ***not receive permission state change updates***. But there's a provision by which
you can help this library to know about permission state changes.

Use [`PermissionFlow#notifyPermissionsChanged()`](https://patilshreyas.github.io/permission-flow-android/docs/permission-flow/dev.shreyaspatil.permissionFlow/-permission-flow/notify-permissions-changed.html) to notify the permission state changes
Expand All @@ -187,14 +173,14 @@ For example:
```kotlin
class MyActivity: AppCompatActivity() {
private val permissionFlow = PermissionFlow.getInstance()

private val permissionLauncher = registerForActivityResult(RequestPermission()) { isGranted ->
permissionFlow.notifyPermissionsChanged(android.Manifest.permission.READ_CONTACTS)
}
}
```

### 6. Manually Start / Stop Listening ⚠️
### 5. Manually Start / Stop Listening ⚠️

This library starts processing things lazily whenever `getPermissionState()` or `getMultiplePermissionState()` is called
for the first time. But this can be controlled with these methods:
Expand All @@ -205,12 +191,48 @@ fun doSomething() {
// This means the state of permission retrieved with [getMultiplePermissionState] method will not
// be updated after stopping listening.
permissionFlow.stopListening()

// Starts listening the changes of state of permissions after stopping listening
permissionFlow.startListening()
}
```

### 6. What about Initialization?

This library automatically gets initialized with the App Startup library.
If you want to provide own coroutine dispatcher

#### 6.1 Initialize **PermissionFlow** as follows (For example, in `Application` class)

```kotlin
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
val permissionDispatcher = Executors.newFixedThreadPool(3).asCoroutineDispatcher()
PermissionFlow.init(this, permissionDispatcher)
}
}
```

#### 6.2 Disable PermissionFlowInitializer in AndroidManifest.xml

Disable auto initialization of library with default configuration using this:

```xml
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">

<meta-data
android:name="dev.shreyaspatil.permissionFlow.initializer.PermissionFlowInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
```


## 📄 API Documentation

[Visit the API documentation](https://patilshreyas.github.io/permission-flow-android/docs/) of this library to get more information in detail. This documentation is generated using [Dokka](https://github.com/Kotlin/dokka).
Expand All @@ -221,11 +243,11 @@ fun doSomething() {

---

## 🙋‍♂️ Contribute
## 🙋‍♂️ Contribute

Read [contribution guidelines](CONTRIBUTING.md) for more information regarding contribution.

## 💬 Discuss?
## 💬 Discuss?

Have any questions, doubts or want to present your opinions, views? You're always welcome. You can [start discussions](https://github.com/PatilShreyas/permission-flow-android/discussions).

Expand Down
15 changes: 8 additions & 7 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ plugins {
}

android {
compileSdk 32
compileSdk 33

defaultConfig {
applicationId "dev.shreyaspatil.permissionFlow.example"
minSdk 21
targetSdk 32
targetSdk 33
versionCode 1
versionName "1.0"

Expand All @@ -36,14 +36,15 @@ android {
}

composeOptions {
kotlinCompilerExtensionVersion composeVersion
kotlinCompilerExtensionVersion composeCompilerVersion
}

packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
namespace 'dev.shreyaspatil.permissionFlow.example'
}

dependencies {
Expand All @@ -52,9 +53,9 @@ dependencies {
implementation(project(":permission-flow-compose"))

// Android
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'androidx.core:core-ktx:1.10.0'
implementation 'androidx.fragment:fragment-ktx:1.5.6'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

Expand All @@ -66,7 +67,7 @@ dependencies {
implementation "androidx.compose.ui:ui-tooling:$composeVersion"

// Lifecycle
def lifecycleVersion = "2.5.0-rc02"
def lifecycleVersion = '2.6.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"

Expand Down
4 changes: 1 addition & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="dev.shreyaspatil.permissionFlow.example">
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Expand All @@ -10,7 +9,6 @@
<uses-permission android:name="android.permission.CAMERA" />

<application
android:name="dev.shreyaspatil.permissionFlow.example.ExampleApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import kotlinx.coroutines.withContext
class AndroidDefaultContactRepository(
private val contentResolver: ContentResolver,
private val permissionFlow: PermissionFlow = PermissionFlow.getInstance(),
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : ContactRepository {

override val allContacts: Flow<List<Contact>> = permissionFlow
Expand All @@ -53,7 +53,7 @@ class AndroidDefaultContactRepository(
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.PHOTO_URI
ContactsContract.CommonDataKinds.Phone.PHOTO_URI,
)

val order = "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} ASC"
Expand All @@ -63,18 +63,18 @@ class AndroidDefaultContactRepository(
projection,
null,
null,
order
order,
)
if (cursor != null) {
while (cursor.moveToNext()) {
val contactId = cursor.getStringOrNull(
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID)
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID),
)
val name = cursor.getStringOrNull(
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME),
)
val number = cursor.getStringOrNull(
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER),
)

if (contactId != null && name != null && number != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private val permissions = arrayOf(
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.READ_CALL_LOG,
android.Manifest.permission.READ_CONTACTS,
android.Manifest.permission.READ_PHONE_STATE
android.Manifest.permission.READ_PHONE_STATE,
)

@Composable
Expand All @@ -63,7 +63,7 @@ fun MainScreen() {
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically),
horizontalAlignment = Alignment.CenterHorizontally
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = { permissionLauncher.launch(permissions) }) {
Text("Request Permissions")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import kotlinx.coroutines.launch

class ContactsViewModel(
private val repository: ContactRepository,
private val permissionFlow: PermissionFlow = PermissionFlow.getInstance()
private val permissionFlow: PermissionFlow = PermissionFlow.getInstance(),
) : ViewModel() {
private val _states = Channel<ContactsUiState>(capacity = BUFFERED)
val state: Flow<ContactsUiState> = _states.receiveAsFlow()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class MultiPermissionActivity : AppCompatActivity() {
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.READ_CALL_LOG,
android.Manifest.permission.READ_CONTACTS,
android.Manifest.permission.READ_PHONE_STATE
android.Manifest.permission.READ_PHONE_STATE,
)

override fun onCreate(savedInstanceState: Bundle?) {
Expand Down
Loading

0 comments on commit 43fe445

Please sign in to comment.