{"id":13604112,"url":"https://github.com/Karumi/Dexter","last_synced_at":"2025-04-11T22:32:40.321Z","repository":{"id":41070623,"uuid":"46348026","full_name":"Karumi/Dexter","owner":"Karumi","description":"Android library that simplifies the process of requesting permissions at runtime.","archived":true,"fork":false,"pushed_at":"2021-07-02T10:55:40.000Z","size":2928,"stargazers_count":5224,"open_issues_count":34,"forks_count":669,"subscribers_count":134,"default_branch":"master","last_synced_at":"2025-03-21T07:35:58.451Z","etag":null,"topics":["android","android-library","permissions"],"latest_commit_sha":null,"homepage":"http://karumi.com","language":"Java","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/Karumi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-11-17T13:14:43.000Z","updated_at":"2025-03-20T11:33:52.000Z","dependencies_parsed_at":"2022-08-30T08:32:09.107Z","dependency_job_id":null,"html_url":"https://github.com/Karumi/Dexter","commit_stats":null,"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Karumi%2FDexter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Karumi%2FDexter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Karumi%2FDexter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Karumi%2FDexter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Karumi","download_url":"https://codeload.github.com/Karumi/Dexter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248489898,"owners_count":21112659,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","android-library","permissions"],"created_at":"2024-08-01T19:00:40.150Z","updated_at":"2025-04-11T22:32:35.306Z","avatar_url":"https://github.com/Karumi.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"![Karumi logo][karumilogo] Dexter [![Build Status](https://travis-ci.org/Karumi/Dexter.svg?branch=master)](https://travis-ci.org/Karumi/Dexter) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.karumi/dexter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.karumi/dexter) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Dexter-green.svg?style=true)](https://android-arsenal.com/details/1/2804)\n======\n\n**This project is no longer under active development. If you are looking for an Android library to be able to request Android permissions in runtime take a look [here](https://android-arsenal.com/tag/235?sort=rating)**\n\nDexter is an Android library that simplifies the process of requesting permissions at runtime.\n\nAndroid Marshmallow includes a new functionality to let users grant or deny permissions when running an app instead of granting them all when installing it. This approach gives the user more control over applications but requires developers to add lots of code to support it.\n\nDexter frees your permission code from your activities and lets you write that logic anywhere you want.\n\n\nScreenshots\n-----------\n\n![Demo screenshot][1]\n\nUsage\n-----\n\n### Dependency\n\nInclude the library in your ``build.gradle``\n\n```groovy\ndependencies{\n    implementation 'com.karumi:dexter:6.2.3'\n}\n```\n\n\nTo start using the library you just need to call `Dexter` with a valid `Context`:\n\n```java\npublic MyActivity extends Activity {\n\t@Override public void onCreate() {\n\t\tsuper.onCreate();\n\t\tDexter.withContext(activity)\n\t\t\t.withPermission(permission)\n\t\t\t.withListener(listener)\n\t\t\t.check();\n\t}\n}\n```\n\n### Single permission\nFor each permission, register a ``PermissionListener`` implementation to receive the state of the request:\n\n```java\nDexter.withContext(this)\n\t.withPermission(Manifest.permission.CAMERA)\n\t.withListener(new PermissionListener() {\n\t\t@Override public void onPermissionGranted(PermissionGrantedResponse response) {/* ... */}\n\t\t@Override public void onPermissionDenied(PermissionDeniedResponse response) {/* ... */}\n\t\t@Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {/* ... */}\n\t}).check();\n```\n\nTo make your life easier we offer some ``PermissionListener`` implementations to perform recurrent actions:\n\n* ``BasePermissionListener`` to make it easier to implement only the methods you want. Keep in mind that you should not call `super` methods when overriding them.\n* ``DialogOnDeniedPermissionListener`` to show a configurable dialog whenever the user rejects a permission request:\n\n```java\nPermissionListener dialogPermissionListener =\n\tDialogOnDeniedPermissionListener.Builder\n\t\t.withContext(context)\n\t\t.withTitle(\"Camera permission\")\n\t\t.withMessage(\"Camera permission is needed to take pictures of your cat\")\n\t\t.withButtonText(android.R.string.ok)\n\t\t.withIcon(R.mipmap.my_icon)\n\t\t.build();\n```\n\n* ``SnackbarOnDeniedPermissionListener`` to show a snackbar message whenever the user rejects a permission request:\n\n```java\nPermissionListener snackbarPermissionListener =\n\tSnackbarOnDeniedPermissionListener.Builder\n\t\t.with(view, \"Camera access is needed to take pictures of your dog\")\n\t\t.withOpenSettingsButton(\"Settings\")\n        .withCallback(new Snackbar.Callback() {\n            @Override\n            public void onShown(Snackbar snackbar) {\n                // Event handler for when the given Snackbar is visible\n            }\n            @Override\n            public void onDismissed(Snackbar snackbar, int event) {\n                // Event handler for when the given Snackbar has been dismissed\n            }\n        }).build();\n```\n\n* ``CompositePermissionListener`` to compound multiple listeners into one:\n\n```java\nPermissionListener snackbarPermissionListener = /*...*/;\nPermissionListener dialogPermissionListener = /*...*/;\nPermissionListener compositePermissionListener = new CompositePermissionListener(snackbarPermissionListener, dialogPermissionListener, /*...*/);\n```\n\n### Multiple permissions\nIf you want to request multiple permissions you just need to call `withPermissions` and register an implementation of ``MultiplePermissionsListener``:\n\n```java\nDexter.withContext(this)\n\t.withPermissions(\n\t\tManifest.permission.CAMERA,\n\t\tManifest.permission.READ_CONTACTS,\n\t\tManifest.permission.RECORD_AUDIO\n\t).withListener(new MultiplePermissionsListener() {\n\t    @Override public void onPermissionsChecked(MultiplePermissionsReport report) {/* ... */}\n\t    @Override public void onPermissionRationaleShouldBeShown(List\u003cPermissionRequest\u003e permissions, PermissionToken token) {/* ... */}\n\t}).check();\n```\n\nThe ``MultiplePermissionsReport`` contains all the details of the permission request like the list of denied/granted permissions or utility methods like ``areAllPermissionsGranted`` and ``isAnyPermissionPermanentlyDenied``.\n\nAs with the single permission listener, there are also some useful implementations for recurring patterns:\n\n* ``BaseMultiplePermissionsListener`` to make it easier to implement only the methods you want. Keep in mind that you should not call `super` methods when overriding them.\n* ``DialogOnAnyDeniedMultiplePermissionsListener`` to show a configurable dialog whenever the user rejects at least one permission:\n\n```java\nMultiplePermissionsListener dialogMultiplePermissionsListener =\n\tDialogOnAnyDeniedMultiplePermissionsListener.Builder\n\t\t.withContext(context)\n\t\t.withTitle(\"Camera \u0026 audio permission\")\n\t\t.withMessage(\"Both camera and audio permission are needed to take pictures of your cat\")\n\t\t.withButtonText(android.R.string.ok)\n\t\t.withIcon(R.mipmap.my_icon)\n\t\t.build();\n```\n\n* ``SnackbarOnAnyDeniedMultiplePermissionsListener`` to show a snackbar message whenever the user rejects any of the requested permissions:\n\n```java\nMultiplePermissionsListener snackbarMultiplePermissionsListener =\n\tSnackbarOnAnyDeniedMultiplePermissionsListener.Builder\n\t\t.with(view, \"Camera and audio access is needed to take pictures of your dog\")\n\t\t.withOpenSettingsButton(\"Settings\")\n        .withCallback(new Snackbar.Callback() {\n            @Override\n            public void onShown(Snackbar snackbar) {\n                // Event handler for when the given Snackbar is visible\n            }\n            @Override\n            public void onDismissed(Snackbar snackbar, int event) {\n                // Event handler for when the given Snackbar has been dismissed\n            }\n        })\n\t\t.build();\n```\n\n* ``CompositePermissionListener`` to compound multiple listeners into one:\n\n```java\nMultiplePermissionsListener snackbarMultiplePermissionsListener = /*...*/;\nMultiplePermissionsListener dialogMultiplePermissionsListener = /*...*/;\nMultiplePermissionsListener compositePermissionsListener = new CompositeMultiplePermissionsListener(snackbarMultiplePermissionsListener, dialogMultiplePermissionsListener, /*...*/);\n```\n\n### Handling listener threads\nIf you want to receive permission listener callbacks on the same thread that fired the permission request, you just need to call ``onSameThread`` before checking for permissions:\n\n```java\nDexter.withContext(context)\n\t.withPermission(permission)\n\t.withListener(listener)\n\t.onSameThread()\n\t.check();\n```\n\n### Showing a rationale\nAndroid will notify you when you are requesting a permission that needs an additional explanation for its usage, either because it is considered dangerous, or because the user has already declined that permission once.\n\nDexter will call the method ``onPermissionRationaleShouldBeShown`` implemented in your listener with a ``PermissionToken``. **It's important to keep in mind that the request process will pause until the token is used**, therefore, you won't be able to call Dexter again or request any other permissions if the token has not been used.\n\nThe most simple implementation of your ``onPermissionRationaleShouldBeShown`` method could be:\n\n```java\n@Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {\n\ttoken.continuePermissionRequest();\n}\n```\n\n### Error handling\nIf you think there is an error in your Dexter integration, just register a `PermissionRequestErrorListener` when calling Dexter:\n\n```java\nDexter.withContext(context)\n\t.withPermission(permission)\n\t.withListener(listener)\n\t.withErrorListener(new PermissionRequestErrorListener() {\n\t\t@Override public void onError(DexterError error) {\n\t\t\tLog.e(\"Dexter\", \"There was an error: \" + error.toString());\n\t\t}\n\t}).check();\n```\n\nThe library will notify you when something bad happens. In general, it is a good practice to, at least, log every error Dexter may throw but is up to you, the developer, to do that.\n\n**IMPORTANT**: Remember to follow the [Google design guidelines][2] to make your application as user-friendly as possible.\n\n### Permission dialog not being shown\n\nIf you are using the ``MultiplePermissionsListener`` and you don't see the permission dialog the second time the permission is checked review your configuration. Keep in mind you need to let Dexter know the rationale you can show was closed or not by calling ``token?.continuePermissionRequest()``. If you don't do this, the next time the permission is requested, the OS dialog asking for this permission won't be shown. You can find more information about this in [here](https://github.com/Karumi/Dexter/issues/105). This is an example of how a multiple permission request should be implemented:\n\n```kotlin\nbutton.setOnClickListener {\n    Dexter.withContext(this@MainActivity)\n                        .withPermissions(\n                            Manifest.permission.ACCESS_COARSE_LOCATION\n                            ,Manifest.permission.ACCESS_FINE_LOCATION)\n                        .withListener(object: MultiplePermissionsListener {\n                            override fun onPermissionsChecked(report: MultiplePermissionsReport?) {\n                                report?.let {\n                                    if(report.areAllPermissionsGranted()){\n                                        toast(\"OK\")\n                                    }\n                                }\n                            }\n                            override fun onPermissionRationaleShouldBeShown(\n                                permissions: MutableList\u003cPermissionRequest\u003e?,\n                                token: PermissionToken?\n                            ) {\n                                // Remember to invoke this method when the custom rationale is closed\n                                // or just by default if you don't want to use any custom rationale.\n                                token?.continuePermissionRequest()\n                            }\n                        })\n                        .withErrorListener {\n                            toast(it.name)\n                        }\n                        .check()\n}\n```\n\nCaveats\n-------\n* For permissions that did not exist before API Level 16, you should check the OS version and use *ContextCompat.checkSelfPermission*. See [You Cannot Hold Non-Existent Permissions](https://commonsware.com/blog/2015/11/09/you-cannot-hold-nonexistent-permissions.html).\n* Don't call Dexter from an Activity with the flag `noHistory` enabled. When a permission is requested, Dexter creates its own Activity internally and pushes it into the stack causing the original Activity to be dismissed.\n* Permissions `SYSTEM_ALERT_WINDOW` and `WRITE_SETTINGS` are considered special by Android. Dexter doesn't handle those and you'll need to request them in the old fashioned way.\n\n# Contributors\n\nThank you all for your work!\n\n| [\u003cimg src=\"https://avatars1.githubusercontent.com/u/3494156?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eCarlos Morera de la Chica\u003c/b\u003e\u003c/sub\u003e](https://github.com/CarlosMChica) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/237122?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAlex Florescu\u003c/b\u003e\u003c/sub\u003e](https://github.com/anothem) | [\u003cimg src=\"https://avatars3.githubusercontent.com/u/416941?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ePedro Veloso\u003c/b\u003e\u003c/sub\u003e](https://github.com/pedronveloso) | [\u003cimg src=\"https://avatars3.githubusercontent.com/u/1636897?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDion Segijn\u003c/b\u003e\u003c/sub\u003e](https://github.com/DanielMartinus) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/837104?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eChristian Panadero\u003c/b\u003e\u003c/sub\u003e](https://github.com/PaNaVTEC) |\n| :---: | :---: | :---: | :---: | :---: |\n| [\u003cimg src=\"https://avatars3.githubusercontent.com/u/6309101?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eVignesh\u003c/b\u003e\u003c/sub\u003e](https://github.com/VigneshPeriasami) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/5009609?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAndy French\u003c/b\u003e\u003c/sub\u003e](https://github.com/AndyFrench) | [\u003cimg src=\"https://avatars2.githubusercontent.com/u/887462?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBernat Borrás Paronella\u003c/b\u003e\u003c/sub\u003e](https://github.com/alorma) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/21121410?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBastien Paul\u003c/b\u003e\u003c/sub\u003e](https://github.com/bastienpaulfr) | [\u003cimg src=\"https://avatars2.githubusercontent.com/u/4190298?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBas Broek\u003c/b\u003e\u003c/sub\u003e](https://github.com/BasThomas) |\n| [\u003cimg src=\"https://avatars2.githubusercontent.com/u/6371716?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBartek Lipinski\u003c/b\u003e\u003c/sub\u003e](https://github.com/blipinsk) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/4202457?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eemmano\u003c/b\u003e\u003c/sub\u003e](https://github.com/emmano) | [\u003cimg src=\"https://avatars1.githubusercontent.com/u/7110368?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eKonrad Morawski\u003c/b\u003e\u003c/sub\u003e](https://github.com/Konrad-Morawski) | [\u003cimg src=\"https://avatars3.githubusercontent.com/u/18151375?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eHrant Alaverdyan\u003c/b\u003e\u003c/sub\u003e](https://github.com/rid-hrant) | [\u003cimg src=\"https://avatars2.githubusercontent.com/u/1730320?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eCarla\u003c/b\u003e\u003c/sub\u003e](https://github.com/iriberri)\n| [\u003cimg src=\"https://avatars1.githubusercontent.com/u/3470701?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ePavel Stepanov\u003c/b\u003e\u003c/sub\u003e](https://github.com/stefan-nsk) | [\u003cimg src=\"https://avatars2.githubusercontent.com/u/16154410?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eEmmett Wilson\u003c/b\u003e\u003c/sub\u003e](https://github.com/EmmettWilson) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/16763485?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMax\u003c/b\u003e\u003c/sub\u003e](https://github.com/TheMaxCoder) | [\u003cimg src=\"https://avatars3.githubusercontent.com/u/18224621?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAl B.\u003c/b\u003e\u003c/sub\u003e](https://github.com/albodelu) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/578021?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eVladislav Bauer\u003c/b\u003e\u003c/sub\u003e](https://github.com/vbauer)\n| [\u003cimg src=\"https://avatars2.githubusercontent.com/u/4047514?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJc Miñarro\u003c/b\u003e\u003c/sub\u003e](https://github.com/JcMinarro) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/944957?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ehandrenliang\u003c/b\u003e\u003c/sub\u003e](https://github.com/handrenliang) | [\u003cimg src=\"https://avatars3.githubusercontent.com/u/19774257?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ejcruzsousa\u003c/b\u003e\u003c/sub\u003e](https://github.com/jcruzsousa) | [\u003cimg src=\"https://avatars0.githubusercontent.com/u/18180559?v=4\" width=\"100px;\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003elzdon\u003c/b\u003e\u003c/sub\u003e](https://github.com/lzdon)\n\nDo you want to contribute?\n--------------------------\n\nFeel free to add any useful feature to the library, we will be glad to improve it with your help.\n\nKeep in mind that your PRs **must** be validated by Travis-CI. Please, run a local build with ``./gradlew checkstyle build test`` before submiting your code.\n\n\nLibraries used in this project\n------------------------------\n\n* [Butterknife][3]\n* [JUnit][4]\n* [Mockito][5]\n\nLicense\n-------\n\n    Copyright 2015 Karumi\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n\n[1]: ./art/sample.gif\n[2]: http://www.google.es/design/spec/patterns/permissions.html\n[3]: https://github.com/JakeWharton/butterknife\n[4]: https://github.com/junit-team/junit\n[5]: https://github.com/mockito/mockito\n[karumilogo]: https://cloud.githubusercontent.com/assets/858090/11626547/e5a1dc66-9ce3-11e5-908d-537e07e82090.png\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKarumi%2FDexter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FKarumi%2FDexter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKarumi%2FDexter/lists"}