{"id":13428687,"url":"https://github.com/afollestad/assent","last_synced_at":"2026-01-14T03:32:51.832Z","repository":{"id":57716565,"uuid":"47073417","full_name":"afollestad/assent","owner":"afollestad","description":"🙏 Android Runtime Permissions made easy and compact, for Kotlin and AndroidX. With coroutines support!","archived":true,"fork":false,"pushed_at":"2022-12-31T02:47:21.000Z","size":497,"stargazers_count":850,"open_issues_count":6,"forks_count":55,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-03-16T01:33:25.971Z","etag":null,"topics":["android","androidx","kotlin","permissions","runtime-permissions"],"latest_commit_sha":null,"homepage":"https://af.codes","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/afollestad.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"afollestad"}},"created_at":"2015-11-29T17:41:11.000Z","updated_at":"2025-02-24T06:23:54.000Z","dependencies_parsed_at":"2023-01-31T17:46:05.897Z","dependency_job_id":null,"html_url":"https://github.com/afollestad/assent","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/afollestad/assent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afollestad%2Fassent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afollestad%2Fassent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afollestad%2Fassent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afollestad%2Fassent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/afollestad","download_url":"https://codeload.github.com/afollestad/assent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afollestad%2Fassent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408843,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","androidx","kotlin","permissions","runtime-permissions"],"created_at":"2024-07-31T01:01:02.882Z","updated_at":"2026-01-14T03:32:51.815Z","avatar_url":"https://github.com/afollestad.png","language":"Kotlin","funding_links":["https://github.com/sponsors/afollestad"],"categories":["Libraries"],"sub_categories":[],"readme":"# Assent\n\nAssent is designed to make Android's runtime permissions easier and take less code in your app to use.\n\n\u003cimg src=\"https://raw.githubusercontent.com/afollestad/assent/master/showcase2.png\" width=\"750\" /\u003e\n\n[![Android CI](https://github.com/afollestad/assent/workflows/Android%20CI/badge.svg)](https://github.com/afollestad/assent/actions?query=workflow%3A%22Android+CI%22)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f1a2334c4c0349699760391bb71f763e)](https://www.codacy.com/app/drummeraidan_50/assent?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=afollestad/assent\u0026amp;utm_campaign=Badge_Grade)\n[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square)](https://www.apache.org/licenses/LICENSE-2.0.html)\n\n## Table of Contents\n\n1. [Gradle Dependency](#gradle-dependency)\n2. [The Basics](#the-basics)\n3. [Using Results](#using-results)\n4. [Permanently Denied](#permanently-denied)\n5. [Request Debouncing](#request-debouncing)\n6. [Rationales](#rationales)\n7. [Coroutines](#coroutines)\n\n---\n\n## Core\n\n[ ![Core](https://img.shields.io/maven-central/v/com.afollestad.assent/core?style=flat\u0026label=Core) ](https://repo1.maven.org/maven2/com/afollestad/assent/core)\n\nAdd this to your module's `build.gradle` file:\n\n```gradle\ndependencies {\n  \n  implementation 'com.afollestad.assent:core:3.0.2'\n}\n```\n\n---\n\n### The Basics\n\nRuntime permissions on Android are completely reliant on the UI the user is in. Permission requests \ngo in and out of Activities and Fragments. This library provides its functionality as Kotlin \nextensions to Fragment Activities (e.g. `AppCompatActivity`) and AndroidX Fragments.\n\n**Note**: *you need to have permissions declared in your `AndroidManifest.xml` too, otherwise \nAndroid will always deny them.*\n\n```kotlin\nclass YourActivity : AppCompatActivity() {\n\n  override fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n\n    // Set to true if one or more permissions are all granted\n    val permissionsGranted: Boolean = isAllGranted(WRITE_EXTERNAL_STORAGE, CAMERA)\n    \n    // Requests one or more permissions, sending the result to a callback\n    askForPermissions(WRITE_EXTERNAL_STORAGE, CAMERA) { result -\u003e\n      // Check the result, see the Using Results section\n    }\n    \n    // Requests one or multiple permissions and performs an action if all are granted\n    runWithPermissions(WRITE_EXTERNAL_STORAGE, CAMERA) { \n      // Do something\n    }\n  }\n}\n```  \n\nAll of the request methods above have an optional `requestCode` named parameter which you can use \nto customize the request code used when dispatching the permission request.\n\n**These methods can all be called from within an Activity or a Fragment. It works the same way in \nboth.**\n\n---\n\n### Using Results\n\n`AssentResult` is provided in request callbacks. It has a few useful fields and methods:\n\n```kotlin\nval result: AssentResult = // ...\n\nval permissions: List\u003cPermission\u003e = result.permissions\nval grantResults: List\u003cGrantResult\u003e = result.grantResults\n\n// Takes a single permission and returns if this result contains it in its set\nval containsPermission: Boolean = result.containsPermission(WRITE_EXTERNAL_STORAGE)\n\n// You can pass multiple permissions as varargs\nval permissionGranted: Boolean = result.isAllGranted(WRITE_EXTERNAL_STORAGE)\n\n// You can pass multiple permissions as varargs\nval permissionDenied: Boolean = result.isAllDenied(WRITE_EXTERNAL_STORAGE)\n\n// Returns GRANTED, DENIED, or PERMANENTLY_DENIED\nval writeStorageGrantResult: GrantResult = result[WRITE_EXTERNAL_STORAGE]\n\nval granted: Set\u003cPermission\u003e = result.granted()\n\nval denied: Set\u003cPermission\u003e = result.denied()\n\nval permanentlyDenied: Set\u003cPermission\u003e = result.permanentlyDenied()\n```\n\n---\n\n### Permanently Denied\n\nAssent detects when the user of your app has permanently denied a permission. Once a permission\nis permanently denied, the Android system will no longer show the permission dialog for that\npermission. At this point, the only way to get them to grant the permission is to explain why you\n_really_ need the permission and then launch system app details page for your app.\n\n```kotlin\nval result: AssentResult = // ...\n\nif (result[WRITE_EXTERNAL_STORAGE] == PERMANENTLY_DENIED) {\n  // NOTE: You should show a dialog of some sort before doing this!\n  showSystemAppDetailsPage()\n}\n```\n\n---\n\n### Request Debouncing\n\nIf you were to do this...\n\n```kotlin\naskForPermissions(WRITE_EXTERNAL_STORAGE) { _ -\u003e }\n\naskForPermissions(WRITE_EXTERNAL_STORAGE) { _ -\u003e }\n```\n\n...the permission would only be requested once, and both callbacks would be called at the same time.\n\nIf you were to do this...\n\n```kotlin\naskForPermissions(WRITE_EXTERNAL_STORAGE) { _ -\u003e }\n\naskForPermissions(CALL_PHONE) { _ -\u003e }\n```\n\n...Assent would wait until the first permission request is done before executing the second request.\n\n---\n\n## Rationales\n\n[ ![Rationales](https://img.shields.io/maven-central/v/com.afollestad.assent/rationales?style=flat\u0026label=Rationales) ](https://repo1.maven.org/maven2/com/afollestad/assent/rationales)\n\nAdd this to your module's `build.gradle` file:\n\n```gradle\ndependencies {\n\n      implementation 'com.afollestad.assent:rationales:3.0.2'\n}\n```\n\n---\n\nGoogle recommends showing rationales for permissions when it may not be obvious to the user why \nyou need them. \n\nAssent supports extensible rationale handlers, it comes with two out-of-the-box: \n* `SnackBarRationaleHandler`\n* `AlertDialogRationaleHandler`\n\n```kotlin\n// Could also use createDialogRationale(...) here, \n// or provide your own implementation of RationaleHandler. \nval rationaleHandler = createSnackBarRationale(rootView) {\n  onPermission(READ_CONTACTS, \"Test rationale #1, please accept!\")\n  onPermission(WRITE_EXTERNAL_STORAGE, \"Test rationale #1, please accept!\")\n  onPermission(READ_SMS, \"Test rationale #3, please accept!\")\n}\n\naskForPermissions(\n    READ_CONTACTS, WRITE_EXTERNAL_STORAGE, READ_SMS,\n    rationaleHandler = rationaleHandler\n) { result -\u003e\n  // Use result\n}\n```\n\n---\n\n## Coroutines\n\n[ ![Coroutines](https://img.shields.io/maven-central/v/com.afollestad.assent/coroutines?style=flat\u0026label=Coroutines) ](https://repo1.maven.org/maven2/com/afollestad/assent/coroutines)\n\nAdd this to your module's `build.gradle` file:\n\n```gradle\ndependencies {\n\n  implementation 'com.afollestad.assent:coroutines:3.0.2'\n}\n```\n\n---\n\nKotlin coroutines enable Assent to work without callbacks. If you do not know the basics of\ncoroutines, you should research them first.\n\nFirst, `awaitPermissionsResult(...)` is the coroutines equivalent to `askForPermissions(...)`:\n\n```kotlin\n// The coroutine extensions work from within any `suspend` function/lambda.\ncoroutineScope {\n   val result: AssentResult = awaitPermissionsResult(\n       READ_CONTACTS, WRITE_EXTERNAL_STORAGE, READ_SMS,\n       rationaleHandler = rationaleHandler\n   )\n   // Use the result...\n}\n```\n\nAnd second, `awaitPermissionsGranted(...)` is the coroutines equivalent to `runWithPermissions(...)`:\n\n```kotlin\n// The coroutine extensions work from within any `suspend` function/lambda.\ncoroutineScope {\n   awaitPermissionsGranted(\n       READ_CONTACTS, WRITE_EXTERNAL_STORAGE, READ_SMS,\n       rationaleHandler = rationaleHandler\n   )\n   // All three permissions were granted...\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafollestad%2Fassent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fafollestad%2Fassent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafollestad%2Fassent/lists"}