master 74b532bf1fe4 cached
185 files
745.7 KB
151.2k tokens
480 symbols
1 requests
Download .txt
Showing preview only (809K chars total). Download the full file or copy to clipboard to get everything.
Repository: permissions-dispatcher/PermissionsDispatcher
Branch: master
Commit: 74b532bf1fe4
Files: 185
Total size: 745.7 KB

Directory structure:
gitextract_y6ni10jf/

├── .github/
│   ├── ISSUE_TEMPLATE.md
│   └── workflows/
│       ├── deploy_snapshot.yml
│       ├── ktx-release.yml
│       ├── pull_request_ci.yml
│       └── release.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── annotation/
│   ├── .gitignore
│   ├── build.gradle
│   ├── gradle.properties
│   └── src/
│       └── main/
│           └── java/
│               └── permissions/
│                   └── dispatcher/
│                       ├── GrantableRequest.java
│                       ├── NeedsPermission.java
│                       ├── OnNeverAskAgain.java
│                       ├── OnPermissionDenied.java
│                       ├── OnShowRationale.java
│                       ├── PermissionRequest.java
│                       └── RuntimePermissions.java
├── build.gradle
├── buildSrc/
│   ├── build.gradle
│   └── src/
│       └── main/
│           └── groovy/
│               └── permissions/
│                   └── dispatcher/
│                       ├── AarToJarDependencyPlugin.groovy
│                       ├── AndroidJarDependencyExtension.groovy
│                       └── AndroidJarDependencyPlugin.groovy
├── doc/
│   ├── java_usage.md
│   ├── maxsdkversion.md
│   ├── migration_guide.md
│   └── special_permissions.md
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── ktx/
│   ├── README.md
│   ├── build.gradle
│   ├── gradle.properties
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   └── java/
│       │       └── permissions/
│       │           └── dispatcher/
│       │               └── ktx/
│       │                   ├── ActivityExtensions.kt
│       │                   ├── Event.kt
│       │                   ├── FragmentExtensions.kt
│       │                   ├── KtxPermissionRequest.kt
│       │                   ├── LocationPermission.kt
│       │                   ├── PermissionRequestFragment.kt
│       │                   ├── PermissionRequestResult.kt
│       │                   ├── PermissionRequestType.kt
│       │                   ├── PermissionRequestViewModel.kt
│       │                   ├── PermissionsRequester.kt
│       │                   ├── PermissionsRequesterImpl.kt
│       │                   └── TypeAliases.kt
│       └── test/
│           ├── java/
│           │   └── permissions/
│           │       └── dispatcher/
│           │           └── test/
│           │               ├── EventTest.kt
│           │               ├── KtxPermissionRequestTest.kt
│           │               ├── LocationPermissionTest.kt
│           │               └── PermissionRequestViewModelTest.kt
│           └── resources/
│               └── mockito-extensions/
│                   └── org.mockito.plugins.MockMaker
├── ktx-sample/
│   ├── .gitignore
│   ├── build.gradle
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── permissions/
│           │       └── dispatcher/
│           │           └── ktx/
│           │               └── sample/
│           │                   ├── MainActivity.kt
│           │                   └── MainFragment.kt
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── fragment_camera.xml
│               │   └── fragment_main.xml
│               └── values/
│                   ├── dimens.xml
│                   ├── strings.xml
│                   └── template-styles.xml
├── library/
│   ├── build.gradle
│   ├── gradle.properties
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   └── java/
│       │       └── permissions/
│       │           └── dispatcher/
│       │               └── PermissionUtils.java
│       └── test/
│           └── java/
│               └── permissions/
│                   └── dispatcher/
│                       └── ApiLevelTestSuite.java
├── lint/
│   ├── .gitignore
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── permissions/
│       │           └── dispatcher/
│       │               ├── CallNeedsPermissionDetector.java
│       │               ├── CallOnRequestPermissionsResultDetector.java
│       │               ├── NoCorrespondingNeedsPermissionDetector.java
│       │               ├── NoDelegateOnResumeDetector.java
│       │               └── PermissionsDispatcherIssueRegistry.java
│       └── test/
│           └── java/
│               └── permissions/
│                   └── dispatcher/
│                       ├── CallNeedsPermissionDetectorKtTest.kt
│                       ├── CallNeedsPermissionDetectorTest.kt
│                       ├── CallOnRequestPermissionsResultDetectorKtTest.kt
│                       ├── CallOnRequestPermissionsResultDetectorTest.kt
│                       ├── NoCorrespondingNeedsPermissionDetectorKtTest.kt
│                       ├── NoCorrespondingNeedsPermissionDetectorTest.kt
│                       ├── NoDelegateOnResumeDetectorKtTest.kt
│                       ├── NoDelegateOnResumeDetectorTest.kt
│                       ├── PermissionsDispatcherIssueRegistryTest.kt
│                       └── Utils.kt
├── processor/
│   ├── build.gradle
│   ├── gradle.properties
│   └── src/
│       ├── main/
│       │   ├── kotlin/
│       │   │   └── permissions/
│       │   │       └── dispatcher/
│       │   │           └── processor/
│       │   │               ├── PermissionsProcessor.kt
│       │   │               ├── ProcessorUnit.kt
│       │   │               ├── RequestCodeProvider.kt
│       │   │               ├── RuntimePermissionsElement.kt
│       │   │               ├── exception/
│       │   │               │   ├── DuplicatedMethodNameException.kt
│       │   │               │   ├── DuplicatedValueException.kt
│       │   │               │   ├── MixPermissionTypeException.kt
│       │   │               │   ├── NoAnnotatedMethodsException.kt
│       │   │               │   ├── NoParametersAllowedException.kt
│       │   │               │   ├── NoThrowsAllowedException.kt
│       │   │               │   ├── PrivateMethodException.kt
│       │   │               │   ├── SpecialPermissionsWithNeverAskAgainException.kt
│       │   │               │   ├── WrongClassException.kt
│       │   │               │   ├── WrongParametersException.kt
│       │   │               │   └── WrongReturnTypeException.kt
│       │   │               ├── impl/
│       │   │               │   ├── java/
│       │   │               │   │   ├── JavaActivityProcessorUnit.kt
│       │   │               │   │   ├── JavaBaseProcessorUnit.kt
│       │   │               │   │   ├── JavaFragmentProcessorUnit.kt
│       │   │               │   │   ├── SensitivePermissionInterface.kt
│       │   │               │   │   ├── SystemAlertWindowHelper.kt
│       │   │               │   │   └── WriteSettingsHelper.kt
│       │   │               │   └── kotlin/
│       │   │               │       ├── KotlinActivityProcessorUnit.kt
│       │   │               │       ├── KotlinBaseProcessorUnit.kt
│       │   │               │       ├── KotlinFragmentProcessorUnit.kt
│       │   │               │       ├── SensitivePermissionInterface.kt
│       │   │               │       ├── SystemAlertWindowHelper.kt
│       │   │               │       └── WriteSettingsHelper.kt
│       │   │               └── util/
│       │   │                   ├── Constants.kt
│       │   │                   ├── Extensions.kt
│       │   │                   ├── Helpers.kt
│       │   │                   └── Validators.kt
│       │   └── resources/
│       │       └── META-INF/
│       │           ├── gradle/
│       │           │   └── incremental.annotation.processors
│       │           └── services/
│       │               └── javax.annotation.processing.Processor
│       └── test/
│           ├── java/
│           │   └── permissions/
│           │       └── dispatcher/
│           │           └── processor/
│           │               ├── KtProcessorTestSuite.kt
│           │               ├── ProcessorTestSuite.java
│           │               ├── base/
│           │               │   ├── AndroidAwareClassLoader.java
│           │               │   ├── BaseTest.java
│           │               │   ├── StringEquals.java
│           │               │   └── TestSuite.java
│           │               ├── data/
│           │               │   └── Source.java
│           │               └── util/
│           │                   └── ExtensionsTest.kt
│           └── resources/
│               └── mockito-extensions/
│                   └── org.mockito.plugins.MockMaker
├── sample/
│   ├── .gitignore
│   ├── build.gradle
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── kotlin/
│           │   └── permissions/
│           │       └── dispatcher/
│           │           └── sample/
│           │               ├── MainActivity.kt
│           │               ├── camera/
│           │               │   ├── CameraPreview.kt
│           │               │   └── CameraPreviewFragment.kt
│           │               └── contacts/
│           │                   └── ContactsFragment.kt
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── fragment_camera.xml
│               │   └── fragment_contacts.xml
│               └── values/
│                   ├── dimens.xml
│                   ├── strings.xml
│                   └── template-styles.xml
├── settings.gradle
└── test/
    ├── .gitignore
    ├── build.gradle
    └── src/
        ├── main/
        │   ├── AndroidManifest.xml
        │   └── java/
        │       └── permissions/
        │           └── dispatcher/
        │               └── test/
        │                   ├── ActivityOnlyNeedsPermission.java
        │                   ├── ActivityWithAllAnnotations.java
        │                   ├── ActivityWithAllAnnotationsKt.kt
        │                   ├── ActivityWithNoParameterArgumentKt.kt
        │                   ├── ActivityWithOnNeverAskAgain.java
        │                   ├── ActivityWithOnPermissionDenied.java
        │                   ├── ActivityWithParametersKt.kt
        │                   ├── ActivityWithShowRationale.java
        │                   ├── ActivityWithSystemAlertWindow.java
        │                   ├── ActivityWithSystemAlertWindowAllAnnotations.java
        │                   ├── ActivityWithSystemAlertWindowKt.kt
        │                   ├── ActivityWithSystemAlertWindowKtAllAnnotations.kt
        │                   ├── ActivityWithWriteSetting.java
        │                   ├── ActivityWithWriteSettingAllAnnotations.java
        │                   ├── ActivityWithWriteSettingKt.kt
        │                   ├── ActivityWithWriteSettingKtAllAnnotations.kt
        │                   ├── FragmentWithAllAnnotations.java
        │                   └── FragmentWithAllAnnotationsKt.kt
        └── test/
            └── java/
                └── permissions/
                    └── dispatcher/
                        └── test/
                            ├── ActivityOnlyNeedsPermissionPermissionsDispatcherTest.kt
                            ├── ActivityWithAllAnnotationsKtPermissionsDispatcherTest.kt
                            ├── ActivityWithAllAnnotationsPermissionsDispatcherTest.kt
                            ├── ActivityWithNoParameterArgumentKtPermissionsDispatcherTest.kt
                            ├── ActivityWithOnNeverAskAgainPermissionsDispatcherTest.kt
                            ├── ActivityWithOnPermissionDeniedPermissionsDispatcherTest.kt
                            ├── ActivityWithParametersKtPermissionsDispatcherTest.kt
                            ├── ActivityWithShowRationalePermissionsDispatcherTest.kt
                            ├── ActivityWithSystemAlertWindowAllAnnotationsPermissionsDispatcherTest.kt
                            ├── ActivityWithSystemAlertWindowKtAllAnnotationsTest.kt
                            ├── ActivityWithSystemAlertWindowKtTest.kt
                            ├── ActivityWithSystemAlertWindowPermissionsDispatcherTest.kt
                            ├── ActivityWithWriteSettingAllAnnotationsPermissionsDispatcherTest.kt
                            ├── ActivityWithWriteSettingKtAllAnnotationsTest.kt
                            ├── ActivityWithWriteSettingKtTest.kt
                            ├── ActivityWithWriteSettingPermissionsDispatcherTest.kt
                            ├── Extensions.kt
                            ├── FragmentWithAllAnnotationsKtPermissionsDispatcherTest.kt
                            └── FragmentWithAllAnnotationsPermissionsDispatcherTest.kt

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
## FAQs

<!--
Before you add the issue, please make sure to read over the following Frequently Asked Questions:

**"XxxPermissionsDispatcher class is not generated"**

- Did you add `@RuntimePermissions` to your class, and `@NeedsPermission` to your method?
- Did you try a Rebuild? (Sync is not Rebuild!)
- Don't mix `apt` and `annotationProcessor`!
- If you're in Kotlin, use `kapt`!
- If you're using Realm 1.x., please consider upgrading to 2.x (refer to Issue #270: https://github.com/hotchemi/PermissionsDispatcher/issues/270)
- If you checked all these steps, and the generated classes are still not showing up, then attach your Activity/Fragment class, and build.gradle.
-->
 
___

## Overview

- Describe the issue briefly

### Expected

- What is the expected behavior?

### Actual

- What is the actual behavior?

## Environment

- Which library version are you using?
- On which devices do you observe the issue?
- Note any other information that might be useful

## Reproducible steps

- While it's not required, it'd be perfect to add a link to a sample project where you encounter the issue


================================================
FILE: .github/workflows/deploy_snapshot.yml
================================================
name: Deploy snapshot

on:
  pull_request:
    branches:
      - master
    types: [closed]

jobs:
  build:
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == true
    steps:
    - uses: actions/checkout@v2
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    - name: Prepare local.properties for testing
      run: echo "sdk.dir=$ANDROID_HOME" > local.properties
    - name: Build with Gradle
      run: ./gradlew clean build
    - name: Deploy snapshot
      run: ./gradlew uploadArchives --no-daemon --no-parallel -PSONATYPE_NEXUS_USERNAME=${{secrets.SONATYPE_NEXUS_USERNAME}} -PSONATYPE_NEXUS_PASSWORD=${{secrets.SONATYPE_NEXUS_PASSWORD}} -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -Psigning.secretKeyRingFile=$(echo ~/.gradle/secring.gpg)


================================================
FILE: .github/workflows/ktx-release.yml
================================================
name: KTX Release

on:
  push:
    tags:
      - 'ktx-*.*.*'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    - name: Prepare local.properties for testing
      run: echo "sdk.dir=$ANDROID_HOME" > local.properties
    - name: Build with Gradle
      run: ./gradlew clean build
    - name: Decode the secret key and place the file
      run: |
        echo "${{secrets.SIGNING_SECRET_KEY_RING_FILE}}" > ~/.gradle/secring.gpg.b64
        base64 -d ~/.gradle/secring.gpg.b64 > ~/.gradle/secring.gpg
    - name: Publish project
      run: ./gradlew ktx:uploadArchives --no-daemon --no-parallel -PSONATYPE_NEXUS_USERNAME=${{secrets.SONATYPE_NEXUS_USERNAME}} -PSONATYPE_NEXUS_PASSWORD=${{secrets.SONATYPE_NEXUS_PASSWORD}} -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -Psigning.secretKeyRingFile=$(echo ~/.gradle/secring.gpg)
    - name: Close the repository
      run: ./gradlew closeAndReleaseRepository


================================================
FILE: .github/workflows/pull_request_ci.yml
================================================
name: CI for pull request

on:
  pull_request:
    types: [assigned, opened, synchronize, reopened]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    - name: Prepare local.properties for testing
      run: echo "sdk.dir=$ANDROID_HOME" > local.properties
    - name: Build with Gradle
      run: ./gradlew clean check --stacktrace


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  push:
    tags:
      - '[0-9]+.[0-9]+.[0-9]+'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    - name: Prepare local.properties for testing
      run: echo "sdk.dir=$ANDROID_HOME" > local.properties
    - name: Build with Gradle
      run: ./gradlew clean build
    - name: Decode the secret key and place the file
      run: |
        echo "${{secrets.SIGNING_SECRET_KEY_RING_FILE}}" > ~/.gradle/secring.gpg.b64
        base64 -d ~/.gradle/secring.gpg.b64 > ~/.gradle/secring.gpg
    - name: Publish project
      run: ./gradlew uploadArchives --no-daemon --no-parallel -PSONATYPE_NEXUS_USERNAME=${{secrets.SONATYPE_NEXUS_USERNAME}} -PSONATYPE_NEXUS_PASSWORD=${{secrets.SONATYPE_NEXUS_PASSWORD}} -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -Psigning.secretKeyRingFile=$(echo ~/.gradle/secring.gpg)
    - name: Close the repository
      run: ./gradlew closeAndReleaseRepository


================================================
FILE: .gitignore
================================================
.gradle
local.properties

build/

.idea/
*.iml


================================================
FILE: CHANGELOG.md
================================================
# ChangeLog

- 4.9.2 | ktx: 1.1.4 2022/04/04
  - Fix: [fix: use ContextCompat instead of PermissionChecker](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/757)
- ktx: 1.1.3 2021/11/07
  - Fix: [sort permissions to secure identification](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/729)
- ktx: 1.1.2 2021/08/14
  - Fix: [permissionRequest proceed doesn't work due to KtxPermissionRequest.requestPermission being null](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/742)
- ktx: 1.1.1 2021/08/09
  - Fix: [Avoid memory leak with ViewModel](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/741)
  - Fix: [Support device orientation again](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/738)
  - Fix: [Write settings action bug fix](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/737)
- 4.9.1 2021/08/09
  - Fix: [Address compile error on Kotlin 1.5](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/733)
- ktx: 1.0.5 2021/04/14
  - Fix: [Use commitAllowingStateLoss() instead of commitNowAllowingStateLoss() in PermissionRequestFragment dismiss() to avoid exception when performing multiple transactions at same time](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/724)
- ktx: 1.0.4 2021/03/16
  - Fix: [Wrong behaviour of ktx library when two permissions are requested in one screen](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/717)
- ktx: 1.0.3 2021/03/03
  - Fix: stop depending on SNAPSHOT library module
- ktx: 1.0.2 2021/03/03
  - Fix: [System dialog doesn't show up, after manually disable permission from setting](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/705)
- ktx: 1.0.1 2020/09/17
  - Fix: [fix: wrap PermissionResult with Event](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/689)
- ktx: 1.0.0 2020/08/31
  - fix: [observe ViewModel only when a permission has not been granted](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/681)
  - feat: [Location dedicated permission methods](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/679)
- 4.8.0 2020/08/31
  - Feat: [Bug fix for NeedOnRequestPermissionsResult lint derector](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/680)
- ktx: 1.0.0-beta1
  - Update: [rewrite internal implementation](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/678)
- 4.7.0 2020/03/25
  - Feat: [ktx module](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/665)
  - Update: [Add missing dangerous permissions check in later versions](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/652)
  - Update: [Use the constant from `PermissionChecker`](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/658)
- 4.6.0 2019/10/30
  - Fix: lint CallNeedsPermission on same named function [#602](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/602)
  - Fix: Remove Conductor support [#644](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/644) 
  [#620](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/620)
  - Fix: Java to Kotlin collections mapping [#643](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/643)
- 4.5.0 2019/07/01
  - Improve: Incremental annotation processing for Kotlin [#626](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/626)
  - Fix: lint CallNeedsPermission on same named function [#602](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/602)
  - Fix: java-kotlin conversion issue [#603](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/603) [#620](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/620)
  - Fix: Remove redundant BuildConfig file [#607](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/607)
- 4.3.1 2019/04/08
  - Add: Add support for internal classes [#574](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/606)
- 4.3.0 2018/12/31
  - Add: Conductor support [#574](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/574)
- 4.2.0 2018/12/21
  - Add: OnShowRationale API change with keeping backward compatibility [#569](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/569)
  - Update: Change maven groupId from `com.github.hotchemi` to `org.permissionsdispatcher` [#560](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/560)
  - Doc: Publish document site! https://permissions-dispatcher.github.io
  - Fix: Any is translated to java.lang.Object [#545](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/545)
  - Add: Support Java incremental compilation [#473](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/473)
  - Update: Drop Xiaomi support [#548](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/548)
- 4.1.0 2018/12/07
  - Fix: compile time validation for detecting OnNeverAskAgain with special permissions [#549](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/549)
  - Fix: Fix CallNeedsPermissionDetector to scan only annotated classes [#536](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/536)
- 4.0.0 2018/10/20
  - Update: [Update androidx ver to 1.0.0](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/530)
  - Fix: [Add NonNull annotation and use requireActivity](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/526)
  - Fix: [workaround to convert java String to kotlin String in argument parameter](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/509)
- 4.0.0-alpha1 2018/07/12
  - Remove: Remove native Fragment support[#498](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/498)
  - Add: Jetpack support [#494](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/494)
- 3.3.2 2018/12/07
  - Update: Drop Xiaomi support [#548](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/548)
- 3.3.1 2018/06/25
  - Internal: Update using Kotlin ver to 1.2.50 [#489](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/489)
  - Add: Add a lint rule for not call WithPermissionCheck inside onResume [#486](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/486)
  - Fix: Fix compile error when request SYSTEM_ALERT_WINDOW on SupportFragment [#482](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/482)
  - Fix: Fix the problem with order matter "Useless @OnShowRationale declaration [#479](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/479)
- 3.2.0 2018/04/17
  - Update: Address lint for Kotlin project [#460](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/460)
  - Add: Add JvmName annotation to generated file [#458](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/458)
  - Update: Deprecate android.app.Fragment [#454](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/454)
  - Fix: Kotlin file problem with CallOnRequestPermissionsResultDetector [#449](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/449)
- 3.1.0 2017/12/18
  - Fix: nullable params with Kotlin [#397](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/397)
  - Fix: PD does not generate kt version of SYSTEM_ALERT_WINDOW [#406](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/406)
- 3.0.1 2017/09/17
  - Fix: NeedsPermission annotated method with parameter doesn't work in Kotlin [#376](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/376)
  - Fix: CallNeedsPermission check incorrectly flags calls to methods of different class [#377](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/377)
- 3.0.0 2017/09/16
  - Add fully [Kotlin support](https://github.com/hotchemi/PermissionsDispatcher/blob/master/doc/kotlin_support.md)!
  - Allow for Deterministic, Reproducible Builds with sorted inputs [#342](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/342)
  - Internal: Migrate Lint Rules to UAST [363](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/363)
  - Rename withCheck to withPermissionCheck [#365](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/365)
  - Fix CallNeedsPermissionDetector to work correctly [#368](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/368)
- 2.4.0 2017/05/01
  - Fix `SupportV13MissingException` with newer Gradle [#279](https://github.com/hotchemi/PermissionsDispatcher/issues/279).
    - Now we bundle support v13 library in the library, you don't have to add v13 dependency by yourself.
      - If you don't need v13 remove it rxpressly. ref: [README](https://github.com/hotchemi/PermissionsDispatcher#download)
  - Remove a workaround in the case `targetSdkVersion < 23` [#305](https://github.com/hotchemi/PermissionsDispatcher/issues/305).
    - If you need the workaround please use  older version.
- 2.3.2 2017/03/10
  - Update minSdkVersion to API level 9 [#286](https://github.com/hotchemi/PermissionsDispatcher/pull/286).
  - Add new compile-time check [#284](https://github.com/hotchemi/PermissionsDispatcher/pull/284).
  - Fix the problem with receiver of startActivityForResult [#280](https://github.com/hotchemi/PermissionsDispatcher/pull/280)
  - Support nested annotated class [#263](https://github.com/hotchemi/PermissionsDispatcher/pull/263)
- 2.3.1 2016/12/26
  - Fix a bug related to Xiaomi. [#240](https://github.com/hotchemi/PermissionsDispatcher/issues/240).
  - Address [#244](https://github.com/hotchemi/PermissionsDispatcher/issues/244) just in case.
- 2.3.0 2016/12/14
  - Start Xiaomi support. [#235](https://github.com/hotchemi/PermissionsDispatcher/pull/235).
  - Fix slight bug for lint check. [#230](https://github.com/hotchemi/PermissionsDispatcher/pull/230).
- 2.2.1 2016/12/08
  - Lint update: Migrate to Psi APIs [#227](https://github.com/hotchemi/PermissionsDispatcher/pull/227).
  - Make sure to support Java 1.6 [#222](https://github.com/hotchemi/PermissionsDispatcher/pull/222).
- 2.2.0 2016/09/25
  - Support maxSdkVersion [#204](https://github.com/hotchemi/PermissionsDispatcher/pull/204).
  - Add ProGuard support [#175](https://github.com/hotchemi/PermissionsDispatcher/pull/175).
  - Some internal cleanup.
- 2.1.3 2016/05/12
  - Fix [#147](https://github.com/hotchemi/PermissionsDispatcher/pull/147).
- 2.1.2 2016/04/11
  - Fix [#131](https://github.com/hotchemi/PermissionsDispatcher/pull/131).
  - Add [#122](https://github.com/hotchemi/PermissionsDispatcher/pull/122).
- 2.1.1 2016/03/30
  - Fix [#124](https://github.com/hotchemi/PermissionsDispatcher/issues/124).
- 2.1.0 2016/03/20
  - Fix [#114](https://github.com/hotchemi/PermissionsDispatcher/issues/114).
- 2.0.9 2016/03/19
  - Fix [#112](https://github.com/hotchemi/PermissionsDispatcher/issues/112).
- 2.0.8 2016/03/09
  - Fix [#107](https://github.com/hotchemi/PermissionsDispatcher/issues/107).
  - Fix [#109](https://github.com/hotchemi/PermissionsDispatcher/issues/109).
- 2.0.7 2016/02/16
  - Kotlin 1.0 support [#98](https://github.com/hotchemi/PermissionsDispatcher/pull/98).
- 2.0.6 2016/02/15
  - Add lint support [#75](https://github.com/hotchemi/PermissionsDispatcher/pull/75).
  - Update kotlin version [#91](https://github.com/hotchemi/PermissionsDispatcher/pull/91).
  - Performance improvement [#93](https://github.com/hotchemi/PermissionsDispatcher/pull/93).
- 2.0.5 2016/01/29
  - Back to support JDK 1.7.
- 2.0.4 2016/01/22
  - Fix [Issue #78](https://github.com/hotchemi/PermissionsDispatcher/issues/78)
- 2.0.3 2016/01/18
 - **This version has a bug which @OnNeverAskAgain is never called. Please don't use it.** 
 - Add [#65](https://github.com/hotchemi/PermissionsDispatcher/pull/65).
 - Add [#72](https://github.com/hotchemi/PermissionsDispatcher/pull/72).
- 2.0.2 2016/01/13
 - Fix [#63](https://github.com/hotchemi/PermissionsDispatcher/issues/63).
- 2.0.1 2015/12/04
    - Add `@NeverAskAgain`.
- 1.2.1 2015/09/14
    - Fix #14 and #16.
- 1.2.0 2015/09/07
    - Add `@DeniedPermission` and `@DeniedPermissions`.
- 1.1.2 2015/08/26
    - Downgrade processor jdk version to 1.7.
- 1.1.1 2015/08/25
    - Fix some bugs.
- 1.1.0 2015/08/24
    - Add `@NeedsPermissions` and `@ShowRationales`.
- 1.0.1 2015/08/20
    - Stop calling rationale methods in the OnRequestPermissionsResult.
- 1.0.0 2015/08/19
    - Using support v4 compat classes.
- 0.9.0 2015/08/18
    - Prepare for preview 3(final release).
- 0.5.0 2015/08/16
    - Initial release.


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

================================================
FILE: README.md
================================================
# PermissionsDispatcher ![CI for pull request](https://github.com/permissions-dispatcher/PermissionsDispatcher/workflows/CI%20for%20pull%20request/badge.svg) [![PermissionsDispatcher](https://www.appbrain.com/stats/libraries/shield/permissions_dispatcher.svg)](https://www.appbrain.com/stats/libraries/details/permissions_dispatcher/permissionsdispatcher)

- **Fully Kotlin/Java support**
- [**Special permissions support**](https://github.com/hotchemi/PermissionsDispatcher/blob/master/doc/special_permissions.md)
- **100% reflection-free**

PermissionsDispatcher provides a simple annotation-based API to handle runtime permissions.

This library lifts the burden that comes with writing a bunch of check statements whether a permission has been granted or not from you, in order to keep your code clean and safe.

## Usage

- Kotlin: You can pick either of [ktx](https://github.com/permissions-dispatcher/PermissionsDispatcher/tree/master/ktx) or [kapt](https://github.com/permissions-dispatcher/PermissionsDispatcher#0-prepare-androidmanifest).
- Java: [apt](https://github.com/hotchemi/PermissionsDispatcher/blob/master/doc/java_usage.md)

Here's a minimum example, in which you register a `MainActivity` which requires `Manifest.permission.CAMERA`.

### 0. Prepare AndroidManifest

Add the following line to `AndroidManifest.xml`:
 
`<uses-permission android:name="android.permission.CAMERA" />`

### 1. Attach annotations

PermissionsDispatcher introduces only a few annotations, keeping its general API concise:

> NOTE: Annotated methods must not be `private`.

|Annotation|Required|Description|
|---|---|---|
|`@RuntimePermissions`|**✓**|Register an `Activity` or `Fragment` to handle permissions|
|`@NeedsPermission`|**✓**|Annotate a method which performs the action that requires one or more permissions|
|`@OnShowRationale`||Annotate a method which explains why the permissions are needed. It passes in a `PermissionRequest` object which can be used to continue or abort the current permission request upon user input. If you don't specify any argument for the method compiler will generate `process${NeedsPermissionMethodName}ProcessRequest` and `cancel${NeedsPermissionMethodName}ProcessRequest`. You can use those methods in place of `PermissionRequest`(ex: with `DialogFragment`)|
|`@OnPermissionDenied`||Annotate a method which is invoked if the user doesn't grant the permissions|
|`@OnNeverAskAgain`||Annotate a method which is invoked if the user chose to have the device "never ask again" about a permission|

```kotlin
@RuntimePermissions
class MainActivity : AppCompatActivity(), View.OnClickListener {

    @NeedsPermission(Manifest.permission.CAMERA)
    fun showCamera() {
        supportFragmentManager.beginTransaction()
                .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
                .addToBackStack("camera")
                .commitAllowingStateLoss()
    }

    @OnShowRationale(Manifest.permission.CAMERA)
    fun showRationaleForCamera(request: PermissionRequest) {
        showRationaleDialog(R.string.permission_camera_rationale, request)
    }

    @OnPermissionDenied(Manifest.permission.CAMERA)
    fun onCameraDenied() {
        Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show()
    }

    @OnNeverAskAgain(Manifest.permission.CAMERA)
    fun onCameraNeverAskAgain() {
        Toast.makeText(this, R.string.permission_camera_never_askagain, Toast.LENGTH_SHORT).show()
    }
}
```

### 2. Delegate to generated functions

Now generated functions become much more concise and intuitive than Java version!

```kotlin
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById(R.id.button_camera).setOnClickListener {
            // NOTE: delegate the permission handling to generated function
            showCameraWithPermissionCheck()
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // NOTE: delegate the permission handling to generated function
        onRequestPermissionsResult(requestCode, grantResults)
    }
```

Check out the [sample](https://github.com/hotchemi/PermissionsDispatcher/tree/master/sample) for more details.

## Other features/plugins

- [Getting Special Permissions](https://github.com/hotchemi/PermissionsDispatcher/blob/master/doc/special_permissions.md)
- [maxSdkVersion](https://github.com/hotchemi/PermissionsDispatcher/blob/master/doc/maxsdkversion.md)
- [IntelliJ plugin](https://github.com/shiraji/permissions-dispatcher-plugin)
- [AndroidAnnotations plugin](https://github.com/AleksanderMielczarek/AndroidAnnotationsPermissionsDispatcherPlugin)

## Installation

**NOTE:**
  - If you're using jCenter we've moved on to MavenCentral, see [migration guide](https://github.com/hotchemi/PermissionsDispatcher/blob/master/doc/migration_guide.md).
  - 4.x only supports [Jetpack](https://developer.android.com/jetpack/). If you still use appcompat 3.x is the way to go.

To add PermissionsDispatcher to your project, include the following in your **app module** `build.gradle` file:

`${latest.version}` is [![Download](https://maven-badges.herokuapp.com/maven-central/com.github.permissions-dispatcher/permissionsdispatcher/badge.svg)](https://search.maven.org/search?q=g:com.github.permissions-dispatcher)

```groovy
dependencies {
  implementation "com.github.permissions-dispatcher:permissionsdispatcher:${latest.version}"
  annotationProcessor "com.github.permissions-dispatcher:permissionsdispatcher-processor:${latest.version}"
}
```

With Kotlin:

```groovy
apply plugin: 'kotlin-kapt'

dependencies {
  implementation "com.github.permissions-dispatcher:permissionsdispatcher:${latest.version}"
  kapt "com.github.permissions-dispatcher:permissionsdispatcher-processor:${latest.version}"
}
```

## License

```
Copyright 2016 Shintaro Katafuchi, Marcel Schnelle, Yoshinori Isogai

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```


================================================
FILE: annotation/.gitignore
================================================
/build


================================================
FILE: annotation/build.gradle
================================================
apply plugin: 'java-library'
apply plugin: "com.vanniktech.maven.publish"


================================================
FILE: annotation/gradle.properties
================================================
POM_NAME        = permissionsdispatcher-annotation
POM_ARTIFACT_ID = permissionsdispatcher-annotation
POM_PACKAGING   = jar


================================================
FILE: annotation/src/main/java/permissions/dispatcher/GrantableRequest.java
================================================
package permissions.dispatcher;

public interface GrantableRequest extends PermissionRequest {
    void grant();
}


================================================
FILE: annotation/src/main/java/permissions/dispatcher/NeedsPermission.java
================================================
package permissions.dispatcher;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Register some methods which permissions are needed.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface NeedsPermission {
    String[] value();

    int maxSdkVersion() default 0;
}

================================================
FILE: annotation/src/main/java/permissions/dispatcher/OnNeverAskAgain.java
================================================
package permissions.dispatcher;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Register some methods handling the user's choice to permanently deny permissions.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface OnNeverAskAgain {
    String[] value();
}


================================================
FILE: annotation/src/main/java/permissions/dispatcher/OnPermissionDenied.java
================================================
package permissions.dispatcher;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Register some methods which permissions are needed.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface OnPermissionDenied {
    String[] value();
}

================================================
FILE: annotation/src/main/java/permissions/dispatcher/OnShowRationale.java
================================================
package permissions.dispatcher;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Register some methods which explain why permissions are needed.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface OnShowRationale {
    String[] value();
}

================================================
FILE: annotation/src/main/java/permissions/dispatcher/PermissionRequest.java
================================================
package permissions.dispatcher;

/**
 * Interface used by {@link OnShowRationale} methods to allow for continuation
 * or cancellation of a permission request.
 */
public interface PermissionRequest {
    void proceed();

    void cancel();
}


================================================
FILE: annotation/src/main/java/permissions/dispatcher/RuntimePermissions.java
================================================
package permissions.dispatcher;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Register an <code>Activity</code> or <code>Fragment</code> to handle permissions.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface RuntimePermissions {
}

================================================
FILE: build.gradle
================================================
buildscript {
    repositories {
        mavenCentral()
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:$GRADLE_PLUGIN_VERSION"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
        classpath "org.jetbrains.dokka:dokka-gradle-plugin:$DOKKA_VERSION"
        classpath "com.vanniktech:gradle-maven-publish-plugin:$PUBLISH_PLUGIN_VERSION"
    }
}

allprojects {
    repositories {
        mavenCentral()
        google()
        jcenter()
        // TODO: remove after publishing kompile-testing to Maven Central
        maven { url 'https://jitpack.io' }
    }
}


================================================
FILE: buildSrc/build.gradle
================================================
apply plugin: "groovy"

repositories {
    mavenCentral()
}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
    implementation "commons-io:commons-io:2.6"
}


================================================
FILE: buildSrc/src/main/groovy/permissions/dispatcher/AarToJarDependencyPlugin.groovy
================================================
package permissions.dispatcher

import org.apache.commons.io.FileUtils
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction

import java.util.zip.ZipFile

/**
 * Custom plugin for creating "broken" JARs from AAR files.
 *
 * It allows an annotation processor's code generation test
 * to resolve Android-specific classes on the classpath.
 *
 * Of course, the generated JARs aren't able to be loaded in by a ClassLoader,
 * since they reference Android classes absent in a pure-JVM environment.
 * However the generated code will compile and serve as the cornerstone
 * for assertions of an annotation processor that interfaces with Android APIs.
 *
 * To do this, a special configuration is created when the plugin is applied.
 * Any dependency specified in this configuration will be written into
 * a specific resource file, then loaded into a custom ClassLoader when
 * the unit tests are executed. Since AARs are not readable by a pure Java
 * runtime environment, their classes need to be extracted, and converted
 * into the JAR format.
 */
class AarToJarConversionPlugin implements Plugin<Project> {

    private static final CONFIGURATION_NAME = "testCompileAar"
    private static final TASK_NAME = "convertAarsToJar"
    private static final CLASSPATH_FILE_NAME = "additional-test-classpath.txt"

    @Override
    void apply(Project project) {
        // Custom Configuration
        def configuration = project.configurations.create(CONFIGURATION_NAME)

        // Conversion Task
        def conversionTask = project.tasks.create(TASK_NAME, ConvertAarToJarTask) {
            inputFiles = configuration
            jarOutputDir = project.file("$project.buildDir/tmp/converted-aars")
            classpathOutputDir = project.file("$project.buildDir/resources/test")
        }

        // Hook into the task dependency chain
        project.tasks.getByName("classes").dependsOn conversionTask
    }

    static class ConvertAarToJarTask extends DefaultTask {

        @Input
        FileCollection inputFiles

        @OutputDirectory
        File jarOutputDir

        @OutputDirectory
        File classpathOutputDir

        @TaskAction
        def convert() {
            def paths = []

            inputFiles.each { file ->
                // AARs need to be converted to JAR,
                // other files are piped through to the output folder
                def destinationFile

                if (file.name.endsWith(".aar")) {
                    // Classes are contained inside a file named classes.jar,
                    // which resides in an AAR.
                    // Extract & rename it according to the artifact in question,
                    // then copy it over to the output
                    def zipFile = new ZipFile(file)
                    def classesEntry = zipFile.getEntry("classes.jar")
                    def newFileName = file.name.replace(".aar", ".jar")
                    def sourceInputStream = zipFile.getInputStream(classesEntry)

                    destinationFile = new File(jarOutputDir, newFileName)
                    FileUtils.copyInputStreamToFile(sourceInputStream, destinationFile)

                    project.logger.info("Converted AAR '$file.name' to JAR")

                } else {
                    destinationFile = new File(jarOutputDir, file.name)
                    FileUtils.copyFile(file, destinationFile)
                }

                paths += destinationFile.toURI().toURL().toString()
            }

            def classpathFile = new File(classpathOutputDir, CLASSPATH_FILE_NAME)
            classpathFile.withWriter { it.write(paths.join("\n")) }
        }
    }
}


================================================
FILE: buildSrc/src/main/groovy/permissions/dispatcher/AndroidJarDependencyExtension.groovy
================================================
package permissions.dispatcher

import javax.annotation.Nullable

class AndroidJarDependencyExtension {
    @Nullable
    String directory
}


================================================
FILE: buildSrc/src/main/groovy/permissions/dispatcher/AndroidJarDependencyPlugin.groovy
================================================
package permissions.dispatcher

import org.gradle.api.Plugin
import org.gradle.api.Project

/**
 * This plugin creates a convenient dependency handler
 * that allows a shorthand way to access an "android.jar" file
 * of the environment's platform.
 *
 * It also adds an extension named "androidJar" to the project,
 * with which the default directory to search can be overridden.
 * If no value is specified explicitly, the plugin performs a
 * lookup of the "sdk.dir" property in the user's local.properties
 * file, and derives the Android version to include from the
 * compile SDK version specified in the root-level gradle.properties.
 */
@SuppressWarnings("GroovyUnusedDeclaration")
class AndroidJarDependencyPlugin implements Plugin<Project> {

    private static final EXTENSION_NAME = "androidJar"

    private static final SDK_DIR_PROPERTY = "sdk.dir"
    private static final PLATFORM_DIR_PROPERTY = "COMPILE_SDK_VERSION"
    private static final ANDROID_JAR_FILENAME = "android.jar"

    @Override
    void apply(Project project) {
        def extension = project.extensions.create(EXTENSION_NAME, AndroidJarDependencyExtension)

        project.dependencies.ext.androidJar = {
            def directory = extension.directory
            if (!directory) {
                // By default, concatenate android.jar path from build environment variables
                def sdkDir = loadProperties(project.rootProject.file("local.properties"))
                        .getProperty(SDK_DIR_PROPERTY)
                def platformDir = loadProperties(project.rootProject.file("gradle.properties"))
                        .getProperty(PLATFORM_DIR_PROPERTY)

                directory = "$sdkDir/platforms/$platformDir"
            }

            if (!new File(directory, ANDROID_JAR_FILENAME).exists()) {
                throw new RuntimeException("can't find android.jar in folder '$directory'!")
            }

            def dependency = project.fileTree(dir: directory, includes: [ANDROID_JAR_FILENAME])
            return project.dependencies.create(dependency)
        }
    }

    private static Properties loadProperties(File file) {
        def properties = new Properties()
        file.withReader { properties.load(it) }
        return properties
    }
}


================================================
FILE: doc/java_usage.md
================================================
## Usage with Java

Here's a minimum example, in which you register a `MainActivity` which requires `Manifest.permission.CAMERA`.

### 0. Prepare AndroidManifest

Add the following line to `AndroidManifest.xml`:
 
`<uses-permission android:name="android.permission.CAMERA" />`

### 1. Attach annotations

PermissionsDispatcher introduces only a few annotations, keeping its general API concise:

> NOTE: Annotated methods must not be `private`.

|Annotation|Required|Description|
|---|---|---|
|`@RuntimePermissions`|**✓**|Register an `Activity` or `Fragment` to handle permissions|
|`@NeedsPermission`|**✓**|Annotate a method which performs the action that requires one or more permissions|
|`@OnShowRationale`||Annotate a method which explains why the permissions are needed. It passes in a `PermissionRequest` object which can be used to continue or abort the current permission request upon user input. If you don't specify any argument for the method compiler will generate `process${NeedsPermissionMethodName}ProcessRequest` and `cancel${NeedsPermissionMethodName}ProcessRequest`. You can use those methods in place of `PermissionRequest`(ex: with `DialogFragment`)|
|`@OnPermissionDenied`||Annotate a method which is invoked if the user doesn't grant the permissions|
|`@OnNeverAskAgain`||Annotate a method which is invoked if the user chose to have the device "never ask again" about a permission|

```java
@RuntimePermissions
public class MainActivity extends AppCompatActivity {

    @NeedsPermission(Manifest.permission.CAMERA)
    void showCamera() {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
                .addToBackStack("camera")
                .commitAllowingStateLoss();
    }

    @OnShowRationale(Manifest.permission.CAMERA)
    void showRationaleForCamera(final PermissionRequest request) {
        new AlertDialog.Builder(this)
            .setMessage(R.string.permission_camera_rationale)
            .setPositiveButton(R.string.button_allow, (dialog, button) -> request.proceed())
            .setNegativeButton(R.string.button_deny, (dialog, button) -> request.cancel())
            .show();
    }

    @OnPermissionDenied(Manifest.permission.CAMERA)
    void showDeniedForCamera() {
        Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show();
    }

    @OnNeverAskAgain(Manifest.permission.CAMERA)
    void showNeverAskForCamera() {
        Toast.makeText(this, R.string.permission_camera_neverask, Toast.LENGTH_SHORT).show();
    }
}
```

### 2. Delegate to generated class

Upon compilation, PermissionsDispatcher generates a class for `MainActivityPermissionsDispatcher`([Activity Name] + PermissionsDispatcher), which you can use to safely access these permission-protected methods.

The only step you have to do is delegating the work to this helper class:

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.button_camera).setOnClickListener(v -> {
      // NOTE: delegate the permission handling to generated method
      MainActivityPermissionsDispatcher.showCameraWithPermissionCheck(this);
    });
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    // NOTE: delegate the permission handling to generated method
    MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
```

Check out the [sample](https://github.com/hotchemi/PermissionsDispatcher/tree/master/sample) for more details.


================================================
FILE: doc/maxsdkversion.md
================================================
## maxSdkVersion

[&lt;uses-permission&gt;](https://developer.android.com/guide/topics/manifest/uses-permission-element.html) has an attribute call `maxSdkVersion`. PermissionsDispatcher support the feature as well.

The following sample is for declaring `Manifest.permisison.WRITE_EXTERNAL_STORAGE` up to API level 18.

### 0. AndroidManifest

Declare the permission with `maxSdkVersion` attribute

```xml
<uses-permission
     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
     android:maxSdkVersion="18" />
```

### 1. Attach annotations with `maxSdkVersion`

```java
@RuntimePermissions
public class MainActivity extends AppCompatActivity {
    @NeedsPermission(value = Manifest.permission.WRITE_EXTERNAL_STORAGE, maxSdkVersion = 18)
    void getStorage() {
    }
}
```


================================================
FILE: doc/migration_guide.md
================================================
# Migration guide

- [Migrating to Maven Central](#migrating-to-maven-central)
- [Migrating to 4.x](#migrating-to-4x)
- [Migrating to 3.x](#migrating-to-3x)
- [Migrating to 2.x](#migrating-to-2x)

## Migrating to Maven Central

Since Maven Central only accepts valid URL, we had no other choices but to change groupId again.

```diff
dependencies {
-  implementation "org.permissionsdispatcher:permissionsdispatcher:${latest.version}"
+  implementation "com.github.permissions-dispatcher:permissionsdispatcher:${latest.version}"
-  annotationProcessor "org.permissionsdispatcher:permissionsdispatcher-processor:${latest.version}"
+  annotationProcessor "com.github.permissions-dispatcher:permissionsdispatcher-processor:${latest.version}"
}
```

#### KTX

NOTE: Due to accidental mistake, artifact id `permissionsdispatcher-ktx` also exists on maven central but `ktx` is the correct one.

```diff
dependencies {
-  implementation "org.permissionsdispatcher:permissionsdispatcher-ktx:${latest.version}"
+  implementation "com.github.permissions-dispatcher:ktx:${latest.version}"
}
```

## Migrating to 4.x

### Change Maven groupId 

Issue: [#560](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/560)

```diff
dependencies {
-  implementation "com.github.hotchemi:permissionsdispatcher:${latest.version}"
+  implementation "org.permissionsdispatcher:permissionsdispatcher:${latest.version}"
-  annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:${latest.version}"
+  annotationProcessor "org.permissionsdispatcher:permissionsdispatcher-processor:${latest.version}"
}
```

### Migrate to AndroidX from AppCompat

Issue: [#488](https://github.com/permissions-dispatcher/PermissionsDispatcher/pull/488)

From 4.x we only support [Jetpack](https://developer.android.com/jetpack/).
Be sure you've gotten AndroidX migration done before upgrading the library version to 4.x.

## Migrating to 3.x

### Method name changing

Issue: [#355](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/355)

PermissionsDispatcher used to generate `***WithCheck` methods.
Starting with 3.0, the suffix of these methods becomes `***WithPermissionCheck`.

```diff
- MainActivityPermissionsDispatcher.showCameraWithCheck(this);
+ MainActivityPermissionsDispatcher.showCameraWithPermissionCheck(this);
```

The motivation of this change is to make generated code more declarative and easy to figure out what'd be going on under the hood.

This change is especially beneficial in Kotlin, because the receiver of the generated method is a class annotated with `@RuntimePermissions`, instead of a helper class named `***PermissionsDispatcher`.

### Kotlin support

Issue: [#320](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/320)

Actually it's been possible to use PermissionsDispatcher with Kotlin already, because of its interoperability with Java. But to give you a more concise and idiomatic experience, we added full Kotlin support which is described in [here](kotlin_support.md).

If you're already using PermissionsDispatcher with Kotlin, be aware of the following 2 changes:

#### `***WithPermissionCheck`

```diff
button.setOnClickListener {
-   MainActivityPermissionsDispatcher.showCameraWithCheck(this)
+   showCameraWithPermissionCheck()
}
```

#### `onRequestPermissionsResult`

```diff
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
-   MainActivityPermissionsDispatcher.onRequestPermissionsResult(requestCode, grantResults)
+   onRequestPermissionsResult(requestCode, grantResults)
}
```

Those methods are defined as extension functions so now you don't have to call `***MainActivityPermissionsDispatcher` class!

## Migrating to 2.x

Since the internals of PermissionsDispatcher 2 have undergone a fundamental refactoring, most notably in the switch of languages to Kotlin for our annotation processor, the exposed APIs to users of the library have been tweaked as well. This guide will help you migrate to the latest version in just a few minutes!

### Update the artifacts

First of all, make sure you're pulling in the correct version of the library:

#### Before
```groovy
dependencies {
    compile "com.github.hotchemi:permissionsdispatcher:1.2.1"
    apt "com.github.hotchemi:permissionsdispatcher-processor:1.2.1"
}
```

#### After
```groovy
dependencies {
    compile "com.github.hotchemi:permissionsdispatcher:2.0.0"
    apt "com.github.hotchemi:permissionsdispatcher-processor:2.0.0"
}
```

*Make sure to change both the library and processor artifacts!*

### @RuntimePermissions

Your `Activity` or `Fragment` classes utilizing PermissionsDispatcher still need to be annotated with `@RuntimePermissions`, so nothing has changed there.

### @NeedsPermission

While version 1.x of the library had two flavors of the `@NeedsPermission` annotation (both the singular and plural form `@NeedsPermissions`), from now on there is only one. Make sure to change your previous usage of `@NeedsPermissions` to the unified one:

#### Before
```java
@NeedsPermissions({ CAMERA, WRITE_EXTERNAL_STORAGE })
void setupCamera() {
}
```

#### After
```java
@NeedsPermission({ CAMERA, WRITE_EXTERNAL_STORAGE })
void setupCamera() {
}
```

### @OnShowRationale

Both `@ShowsRationale` and its plural form `@ShowsRationales` have been removed in version 2. They have been unified in the new annotation `@OnShowRationale`, which is used to display a reasoning to your users about why you need to request permissions for specific actions. The signature of methods annotated with this new annotation require a single parameter of type `PermissionRequest`: When PermissionsDispatcher calls this method on your class, the `PermissionRequest` will provide an interface for you to either `cancel()` or `proceed()` the ongoing runtime permission process. This allows you to defer showing a system dialog, for instance if you would like to display your own explanation in a dialog beforehand. And don't worry: If you forget to follow this signature pattern, PermissionsDispatcher will remind you at compile time! This is one of the most exciting new features of PermissionsDispatcher 2:

#### Before
```java
@ShowsRationale({ CAMERA, WRITE_EXTERNAL_STORAGE })
void showCameraRationale() {
    // Can't really do much here, since the system dialog is shown immediately afterwards...
    Toast.makeText(...).show();
}
```

#### After
```java
@OnShowRationale({ CAMERA, WRITE_EXTERNAL_STORAGE })
void showCameraRationale(final PermissionRequest request) {
    // E.g. show a dialog explaining why you need the permission.
    // Call proceed() or cancel() on the incoming request to continue or abort the current permissions process
    new AlertDialog.Builder(...)
        .setPositiveButton("OK", (dialog, which) -> request.proceed())
        .setNegativeButton("Abort", (dialog, which) -> request.cancel())
        .show();
}
```

### @OnPermissionDenied

The old annotations `@DeniedPermission` and `@DeniedPermissions` have been unified into the new annotation `@OnPermissionDenied`. A method annotated with this annotation will be invoked if the user declines an ongoing permission request. For developers, it's as simple as switching the annotation's name:

#### Before
```java
@DeniedPermission({ CAMERA, WRITE_EXTERNAL_STORAGE })
void cameraDenied() {
}
```

#### After
```java
@OnPermissionDenied({ CAMERA, WRITE_EXTERNAL_STORAGE })
void cameraDenied() {
}
```

### That's it!

You're good to go and have successfully migrated to PermissionsDispatcher 2.x! We are striving to continue improving this library, so that the hassle of runtime permission handling becomes as convenient as possible for you.


================================================
FILE: doc/special_permissions.md
================================================
## Special Permissions

PermissionsDispatcher takes care of special permissions `Manifest.permission.SYSTEM_ALERT_WINDOW` and `Manifest.permission.WRITE_SETTINGS`.

The following sample is to grant `SYSTEM_ALERT_WINDOW`.

### 0. Prepare AndroidManifest

Add the following line to `AndroidManifest.xml`:
 
`<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />`

### 1. Attach annotations

It's the same as other permissions:

```java
@RuntimePermissions
public class MainActivity extends AppCompatActivity {

    @NeedsPermission(Manifest.permission.SYSTEM_ALERT_WINDOW)
    void systemAlertWindow() {
    }

    @OnShowRationale(Manifest.permission.SYSTEM_ALERT_WINDOW)
    void systemAlertWindowOnShowRationale(final PermissionRequest request) {
    }

    @OnPermissionDenied(Manifest.permission.SYSTEM_ALERT_WINDOW)
    void systemAlertWindowOnPermissionDenied() {
    }

    @OnNeverAskAgain(Manifest.permission.SYSTEM_ALERT_WINDOW)
    void systemAlertWindowOnNeverAskAgain() {
    }
}
```

### 2. Delegate to generated class

Unlike other permissions, special permissions require to call the delegation method at `onActivityResult`:

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.button_system_alert_window).setOnClickListener(v -> {
      // NOTE: delegate the permission handling to generated method
      MainActivityPermissionsDispatcher.systemAlertWindowWithPermissionCheck(this);
    });
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    MainActivityPermissionsDispatcher.onActivityResult(this, requestCode);
}
```


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists


================================================
FILE: gradle.properties
================================================
# Upload configuration
USER                    = hotchemi
GROUP                   = com.github.permissions-dispatcher
VERSION_NAME            = 4.10.0-SNAPSHOT
POM_NAME                = PermissionsDispatcher
POM_DESCRIPTION         = A declarative API to handle Android runtime permissions.
POM_INCEPTION_YEAR      = 2015
POM_URL                 = https://github.com/permissions-dispatcher/PermissionsDispatcher
POM_SCM_URL             = https://github.com/permissions-dispatcher/PermissionsDispatcher
POM_SCM_CONNECTION      = https://github.com/permissions-dispatcher/PermissionsDispatcher.git
POM_LICENCE_NAME        = The Apache Software License, Version 2.0
POM_LICENCE_URL         = https://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST        = repo
POM_DEVELOPER_ID        = hotchemi
POM_DEVELOPER_NAME      = Shintaro Katafuchi
POM_DEVELOPER_URL       = https://github.com/hotchemi

# Plugin versions
DOKKA_VERSION           = 1.4.20
PUBLISH_PLUGIN_VERSION  = 0.18.0
GRADLE_PLUGIN_VERSION   = 4.2.0
KOTLIN_VERSION          = 1.5.20
KOTLIN_METADATA_VERSION = 0.3.0
ANDROIDX_LIBRARY_VERSION= 1.0.0
LIFECYCLE_VERSION       = 2.2.0
ARC_TESTING_VERSION     = 1.1.1
JAVAPOET_VERSION        = 1.9.0
KOTLINPOET_VERSION      = 1.3.0
JUNIT_VERSION           = 4.12
MOCKITO_VERSION         = 2.28.2
MOCKITO_KOTLIN_VERSION  = 2.2.0
POWERMOCK_VERSION       = 2.0.2
COMPILE_TESTING_VERSION = 0.12
LINT_VERSION            = 26.3.2
ROBOLECTRIC_VERSION     = 3.3.2
COMMONS_IO_VERSION      = 2.6
KOMPILE_TESTING_VERSION = 0.1.4

# Android configuration
COMPILE_SDK_VERSION     = android-29
TARGET_SDK_VERSION      = 29
MIN_SDK_VERSION         = 14
SAMPLE_MIN_SDK_VERSION  = 16

# Gradle parameters
org.gradle.daemon       = true
org.gradle.jvmargs      = -XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m

# AndroidX
android.useAndroidX     = true
android.enableJetifier  = true


================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: ktx/README.md
================================================
## permissionsdispatcher-ktx

`permissionsdispatcher-ktx` aims to let developers cope with runtime permissions handling in a declarative style without using annotation processing([kapt](https://kotlinlang.org/docs/reference/kapt.html)).

Let's see a minimum example, in which you register a `MainActivity` which requires `Manifest.permission.CAMERA`.

### 0. Declare a permission in AndroidManifest.xml

Add the following line to `AndroidManifest.xml`:
 
`<uses-permission android:name="android.permission.CAMERA" />`

### 1. Define a requester with `constructPermissionsRequest`

The library provides `constructPermissionsRequest` which you can construct a requester object with the given several callback functions to be called in an appropriate situation.
 
```kotlin
/**
 * @param permissions the permissions [requiresPermission] requires.
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param onNeverAskAgain the method invoked if the user does not deny the permissions with
 * "never ask again" option.
 * @param requiresPermission the action requires [permissions].
 */
fun FragmentActivity/*(or Fragment)*/.constructPermissionsRequest(
    vararg permissions: String,
    onShowRationale: ShowRationaleFunc? = null,
    onPermissionDenied: Func? = null,
    onNeverAskAgain: Func? = null,
    requiresPermission: Func): PermissionsRequester
```

Here you just define `showCamera` and basically that's it! With the library you don't need to manually override `onRequestPermissionsResult`.

NOTE: Be sure to construct a requester every time an activity is created to capture the callbacks appropriately.

```kotlin
class MainActivity: AppCompatActivity {
    // constructPermissionsRequest must be invoked every time an activity is created 
    private val showCamera = constructPermissionsRequest(Manifest.permission.CAMERA,
        onShowRationale = ::onCameraShowRationale,
        onPermissionDenied = ::onCameraDenied,
        onNeverAskAgain = ::onCameraNeverAskAgain) {
		    // do something here
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById(R.id.button_camera).setOnClickListener {
            showCamera.launch()
        }
    }

    private fun onCameraDenied() {
        Toast.makeText(requireContext(), R.string.permission_camera_denied, Toast.LENGTH_SHORT).show()
    }

    private fun onCameraShowRationale(request: PermissionRequest) {
        request.proceed()
    }

    private fun onCameraNeverAskAgain() {
        Toast.makeText(requireContext(), R.string.permission_camera_never_ask_again, Toast.LENGTH_SHORT).show()
    }
}
```

Check out the [sample](https://github.com/hotchemi/PermissionsDispatcher/tree/master/ktx-sample) for more details.

#### Location Permissions

Since the location permissions have been one of the most sensitive permission group to deal with, we provide a dedicated method `constructLocationPermissionRequest`.
With the method you don't have to think of which API version you can ask [ACCESS_BACKGROUND_LOCATION](https://developer.android.com/about/versions/10/privacy/changes#app-access-device-location)(see the [issue](https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/646) for more detail).

```kotlin
/**
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 */
fun FragmentActivity/*(or Fragment)*/.constructLocationPermissionRequest(
    vararg permissions: LocationPermission,
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    onNeverAskAgain: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester
```
 
#### Special Permissions

The library also provides `constructWriteSettingsPermissionRequest` and
 `constructSystemAlertWindowPermissionRequest` to support `WRITE_SETTINGS` and `SYSTEM_ALERT_WINDOW` that
 requires exceptional handling.

```kotlin
/**
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 */
fun FragmentActivity/*(or Fragment)*/.constructWriteSettingsPermissionRequest(
    onShowRationale: ShowRationaleFunc? = null,
    onPermissionDenied: Func? = null,
    requiresPermission: Func): PermissionsRequester

 /**
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 */
fun FragmentActivity/*(or Fragment)*/.constructSystemAlertWindowPermissionRequest(
    onShowRationale: ShowRationaleFunc? = null,
    onPermissionDenied: Func? = null,
    requiresPermission: Func): PermissionsRequester
```

## Installation

`${latest.version}` is [![Download](https://maven-badges.herokuapp.com/maven-central/com.github.permissions-dispatcher/ktx/badge.svg)](https://search.maven.org/artifact/com.github.permissions-dispatcher/ktx/1.0.2/aar)

```groovy
dependencies {
  implementation "com.github.permissions-dispatcher:ktx:${latest.version}"
}
```


================================================
FILE: ktx/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'org.jetbrains.dokka'
apply plugin: "com.vanniktech.maven.publish"

android {
    compileSdkVersion COMPILE_SDK_VERSION
    defaultConfig {
        minSdkVersion MIN_SDK_VERSION
        targetSdkVersion TARGET_SDK_VERSION
    }

    libraryVariants.all {
        it.generateBuildConfigProvider.configure { enabled = false }
    }
}

dependencies {
    api "com.github.permissions-dispatcher:permissionsdispatcher:4.8.0"

    implementation "androidx.core:core:$ANDROIDX_LIBRARY_VERSION"
    implementation "androidx.appcompat:appcompat:$ANDROIDX_LIBRARY_VERSION"
    implementation "androidx.fragment:fragment:$ANDROIDX_LIBRARY_VERSION"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION"
    implementation "androidx.collection:collection:$ANDROIDX_LIBRARY_VERSION"
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$KOTLIN_VERSION"

    testImplementation "junit:junit:$JUNIT_VERSION"
    testImplementation "androidx.test:core:1.3.0-rc01"
    testImplementation "androidx.test.ext:junit:1.1.2-rc01"
    testImplementation "androidx.test:runner:1.3.0-rc01"
    testImplementation "android.arch.core:core-testing:$ARC_TESTING_VERSION"
    testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$MOCKITO_KOTLIN_VERSION"
}


================================================
FILE: ktx/gradle.properties
================================================
POM_NAME        = ktx
POM_ARTIFACT_ID = ktx
POM_PACKAGING   = aar
VERSION_NAME    = 1.2.0-SNAPSHOT


================================================
FILE: ktx/src/main/AndroidManifest.xml
================================================
<manifest package="permissions.dispatcher.ktx"/>


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/ActivityExtensions.kt
================================================
package permissions.dispatcher.ktx

import android.annotation.SuppressLint
import android.provider.Settings
import androidx.fragment.app.FragmentActivity

/**
 * Constructs a request for ordinary permissions that require a grant from the user.
 * Be sure to invoke the method when an activity is created to capture the valid callbacks.
 *
 * @param permissions the permissions [requiresPermission] requires.
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param onNeverAskAgain the method invoked if the user does not deny the permissions with
 * "never ask again" option.
 * @param requiresPermission the action requires [permissions].
 * @see PermissionsRequester
 */
fun FragmentActivity.constructPermissionsRequest(
    vararg permissions: String,
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    onNeverAskAgain: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester = PermissionsRequesterImpl(
    permissions = permissions,
    activity = this,
    onShowRationale = onShowRationale,
    onPermissionDenied = onPermissionDenied,
    onNeverAskAgain = onNeverAskAgain,
    requiresPermission = requiresPermission,
    permissionRequestType = PermissionRequestType.Normal
)

/**
 * Constructs a request for location permissions that require a grant from the user.
 * Be sure to invoke the method when an activity is created to capture the valid callbacks.
 *
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 * @see PermissionsRequester
 */
fun FragmentActivity.constructLocationPermissionRequest(
    vararg permissions: LocationPermission,
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    onNeverAskAgain: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester = PermissionsRequesterImpl(
    permissions = permissions.filterByApiLevel(),
    activity = this,
    onShowRationale = onShowRationale,
    onPermissionDenied = onPermissionDenied,
    onNeverAskAgain = onNeverAskAgain,
    requiresPermission = requiresPermission,
    permissionRequestType = PermissionRequestType.Normal
)

/**
 * Constructs a request for [android.Manifest.permission.WRITE_SETTINGS].
 * Be sure to invoke the method when an activity is created to capture the valid callbacks.
 *
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 * @see PermissionsRequester
 */
@SuppressLint("InlinedApi")
fun FragmentActivity.constructWriteSettingsPermissionRequest(
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester = PermissionsRequesterImpl(
    permissions = arrayOf(Settings.ACTION_MANAGE_WRITE_SETTINGS),
    activity = this,
    onShowRationale = onShowRationale,
    onPermissionDenied = onPermissionDenied,
    onNeverAskAgain = null,
    requiresPermission = requiresPermission,
    permissionRequestType = PermissionRequestType.WriteSettings
)

/**
 * Constructs a request for [android.Manifest.permission.SYSTEM_ALERT_WINDOW].
 * Be sure to invoke the method when an activity is created to capture the valid callbacks.
 *
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 * @see PermissionsRequester
 */
@SuppressLint("InlinedApi")
fun FragmentActivity.constructSystemAlertWindowPermissionRequest(
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester = PermissionsRequesterImpl(
    permissions = arrayOf(Settings.ACTION_MANAGE_OVERLAY_PERMISSION),
    activity = this,
    onShowRationale = onShowRationale,
    onPermissionDenied = onPermissionDenied,
    onNeverAskAgain = null,
    requiresPermission = requiresPermission,
    permissionRequestType = PermissionRequestType.SystemAlertWindow
)


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/Event.kt
================================================
package permissions.dispatcher.ktx

import androidx.annotation.AnyThread

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 * ref: https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
 */
@AnyThread
class Event<out T>(private val content: T) {
    @Volatile
    private var hasBeenHandled = false

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? = if (hasBeenHandled) {
        null
    } else {
        hasBeenHandled = true
        content
    }
}


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/FragmentExtensions.kt
================================================
package permissions.dispatcher.ktx

import android.annotation.SuppressLint
import android.provider.Settings
import androidx.fragment.app.Fragment

/**
 * Constructs a request for ordinary permissions that require a grant from the user.
 * Be sure to invoke the method when an activity is created to capture the valid callbacks.
 *
 * @param permissions the permissions [requiresPermission] requires.
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param onNeverAskAgain the method invoked if the user does not deny the permissions with
 * "never ask again" option.
 * @param requiresPermission the action requires [permissions].
 * @see PermissionsRequester
 */
fun Fragment.constructPermissionsRequest(
    vararg permissions: String,
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    onNeverAskAgain: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester = PermissionsRequesterImpl(
    permissions = permissions,
    activity = requireActivity(),
    onShowRationale = onShowRationale,
    onPermissionDenied = onPermissionDenied,
    onNeverAskAgain = onNeverAskAgain,
    requiresPermission = requiresPermission,
    permissionRequestType = PermissionRequestType.Normal
)

/**
 * Constructs a request for location permissions that require a grant from the user.
 * Be sure to invoke the method when an activity is created to capture the valid callbacks.
 *
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 * @see PermissionsRequester
 */
fun Fragment.constructLocationPermissionRequest(
    vararg permissions: LocationPermission,
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    onNeverAskAgain: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester = PermissionsRequesterImpl(
    permissions = permissions.filterByApiLevel(),
    activity = requireActivity(),
    onShowRationale = onShowRationale,
    onPermissionDenied = onPermissionDenied,
    onNeverAskAgain = onNeverAskAgain,
    requiresPermission = requiresPermission,
    permissionRequestType = PermissionRequestType.Normal
)

/**
 * Constructs a request for [android.Manifest.permission.WRITE_SETTINGS].
 * Be sure to invoke the method when an activity is created to capture the valid callbacks.
 *
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 * @see PermissionsRequester
 */
@SuppressLint("InlinedApi")
fun Fragment.constructWriteSettingsPermissionRequest(
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester = PermissionsRequesterImpl(
    permissions = arrayOf(Settings.ACTION_MANAGE_WRITE_SETTINGS),
    activity = requireActivity(),
    onShowRationale = onShowRationale,
    onPermissionDenied = onPermissionDenied,
    onNeverAskAgain = null,
    requiresPermission = requiresPermission,
    permissionRequestType = PermissionRequestType.WriteSettings
)

/**
 * Constructs a request for [android.Manifest.permission.SYSTEM_ALERT_WINDOW].
 * Be sure to invoke the method when an activity is created to capture the valid callbacks.
 *
 * @param onShowRationale the method explains why the permissions are required.
 * @param onPermissionDenied the method invoked if the user doesn't grant the permissions.
 * @param requiresPermission the action requires [permissions].
 * @see PermissionsRequester
 */
@SuppressLint("InlinedApi")
fun Fragment.constructSystemAlertWindowPermissionRequest(
    onShowRationale: ShowRationaleFun? = null,
    onPermissionDenied: Fun? = null,
    requiresPermission: Fun
): PermissionsRequester = PermissionsRequesterImpl(
    permissions = arrayOf(Settings.ACTION_MANAGE_OVERLAY_PERMISSION),
    activity = requireActivity(),
    onShowRationale = onShowRationale,
    onPermissionDenied = onPermissionDenied,
    onNeverAskAgain = null,
    requiresPermission = requiresPermission,
    permissionRequestType = PermissionRequestType.SystemAlertWindow
)


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/KtxPermissionRequest.kt
================================================
package permissions.dispatcher.ktx

import permissions.dispatcher.PermissionRequest
import java.lang.ref.WeakReference

internal class KtxPermissionRequest(
    private val requestPermission: WeakReference<Fun>,
    private val permissionDenied: WeakReference<Fun>?
) : PermissionRequest {
    override fun proceed() {
        requestPermission.get()?.invoke()
    }

    override fun cancel() {
        permissionDenied?.get()?.invoke()
    }

    companion object {
        fun create(onPermissionDenied: Fun?, requestPermission: Fun) = KtxPermissionRequest(
            requestPermission = WeakReference(requestPermission),
            permissionDenied = onPermissionDenied?.let { WeakReference(it) }
        )
    }
}


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/LocationPermission.kt
================================================
package permissions.dispatcher.ktx

import android.Manifest
import android.annotation.SuppressLint
import android.os.Build

/**
 * An enum which represents location permission types.
 *
 * **See:** [Android Developers](https://developer.android.com/training/location/permissions)
 */
enum class LocationPermission(internal val permission: String, internal val apiLevel: Int) {
    FINE(Manifest.permission.ACCESS_FINE_LOCATION, 1),
    COARSE(Manifest.permission.ACCESS_COARSE_LOCATION, 1),

    @SuppressLint("InlinedApi")
    BACKGROUND(Manifest.permission.ACCESS_BACKGROUND_LOCATION, 29)
}

internal fun Array<out LocationPermission>.filterByApiLevel(sdkVer: Int = Build.VERSION.SDK_INT) =
    asSequence().filter { it.apiLevel <= sdkVer }.map { it.permission }.toList().toTypedArray()


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/PermissionRequestFragment.kt
================================================
package permissions.dispatcher.ktx

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import permissions.dispatcher.PermissionUtils
import permissions.dispatcher.PermissionUtils.verifyPermissions
import java.util.*

internal sealed class PermissionRequestFragment : Fragment() {
    protected val requestCode = Random().nextInt(1000)
    protected lateinit var viewModel: PermissionRequestViewModel

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        retainInstance = true
        viewModel = ViewModelProvider(requireActivity()).get(PermissionRequestViewModel::class.java)
    }

    protected fun dismiss() =
        fragmentManager?.beginTransaction()?.remove(this)?.commitAllowingStateLoss()

    internal class NormalRequestPermissionFragment : PermissionRequestFragment() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val permissions = arguments?.getStringArray(BUNDLE_PERMISSIONS_KEY) ?: return
            requestPermissions(permissions, requestCode)
        }

        override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<String>,
            grantResults: IntArray
        ) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            if (requestCode == this.requestCode) {
                // https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/729
                val key = permissions.sortedArray().contentToString()
                if (verifyPermissions(*grantResults)) {
                    viewModel.postPermissionRequestResult(key, PermissionResult.GRANTED)
                } else {
                    if (!PermissionUtils.shouldShowRequestPermissionRationale(this, *permissions)) {
                        viewModel.postPermissionRequestResult(
                            key,
                            PermissionResult.DENIED_AND_DISABLED
                        )
                    } else {
                        viewModel.postPermissionRequestResult(key, PermissionResult.DENIED)
                    }
                }
            }
            dismiss()
        }

        companion object {
            const val BUNDLE_PERMISSIONS_KEY = "key:permissions"

            fun newInstance(permissions: Array<out String>) =
                NormalRequestPermissionFragment().apply {
                    arguments = Bundle().apply {
                        putStringArray(BUNDLE_PERMISSIONS_KEY, permissions)
                    }
                }
        }
    }

    internal class SpecialRequestPermissionFragment : PermissionRequestFragment() {
        private lateinit var action: String

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            action = arguments?.getString(BUNDLE_ACTION_KEY) ?: return
            val packageName = context?.packageName ?: return
            val uri = Uri.parse("package:$packageName")
            startActivityForResult(Intent(action, uri), requestCode)
        }

        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            if (requestCode == this.requestCode) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                    && Settings.canDrawOverlays(activity)
                ) {
                    viewModel.postPermissionRequestResult(action, PermissionResult.GRANTED)
                } else {
                    viewModel.postPermissionRequestResult(action, PermissionResult.DENIED)
                }
            }
            dismiss()
        }

        companion object {
            const val BUNDLE_ACTION_KEY = "key:action"

            fun newInstance(action: String) = SpecialRequestPermissionFragment().apply {
                arguments = Bundle().apply {
                    putString(BUNDLE_ACTION_KEY, action)
                }
            }
        }
    }
}


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/PermissionRequestResult.kt
================================================
package permissions.dispatcher.ktx

internal enum class PermissionResult {
    GRANTED,
    DENIED,
    DENIED_AND_DISABLED
}


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/PermissionRequestType.kt
================================================
package permissions.dispatcher.ktx

import android.content.Context
import android.os.Build
import android.provider.Settings
import androidx.annotation.RequiresApi
import permissions.dispatcher.PermissionUtils.hasSelfPermissions

internal sealed class PermissionRequestType {
    object Normal : PermissionRequestType() {
        override fun checkPermissions(context: Context, permissions: Array<out String>): Boolean =
            hasSelfPermissions(context, *permissions)

        override fun fragment(permissions: Array<out String>): PermissionRequestFragment =
            PermissionRequestFragment.NormalRequestPermissionFragment.newInstance(
                permissions
            )
    }

    object SystemAlertWindow : PermissionRequestType() {
        override fun checkPermissions(context: Context, permissions: Array<out String>): Boolean =
            Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context)

        @RequiresApi(Build.VERSION_CODES.M)
        override fun fragment(permissions: Array<out String>): PermissionRequestFragment =
            PermissionRequestFragment.SpecialRequestPermissionFragment.newInstance(
                Settings.ACTION_MANAGE_OVERLAY_PERMISSION
            )
    }

    object WriteSettings : PermissionRequestType() {
        override fun checkPermissions(context: Context, permissions: Array<out String>): Boolean =
            Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.System.canWrite(context)

        @RequiresApi(Build.VERSION_CODES.M)
        override fun fragment(permissions: Array<out String>): PermissionRequestFragment =
            PermissionRequestFragment.SpecialRequestPermissionFragment.newInstance(
                Settings.ACTION_MANAGE_WRITE_SETTINGS
            )
    }

    abstract fun checkPermissions(context: Context, permissions: Array<out String>): Boolean

    abstract fun fragment(permissions: Array<out String>): PermissionRequestFragment
}


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/PermissionRequestViewModel.kt
================================================
package permissions.dispatcher.ktx

import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import java.lang.ref.WeakReference

@MainThread
internal class PermissionRequestViewModel : ViewModel() {
    private val permissionRequestResult =
        MutableLiveData<MutableMap<String, Event<PermissionResult>>>()
        get() {
            field.value ?: run { field.value = mutableMapOf() }
            return field
        }

    fun postPermissionRequestResult(key: String, value: PermissionResult) {
        permissionRequestResult.value?.set(key, Event(value))
        permissionRequestResult.notifyObserver()
    }

    inline fun observe(
        owner: LifecycleOwner,
        key: String,
        requiresPermission: WeakReference<Fun>,
        onPermissionDenied: WeakReference<Fun>?,
        onNeverAskAgain: WeakReference<Fun>?
    ) {
        permissionRequestResult.observe(owner, {
            when (it[key]?.getContentIfNotHandled()) {
                PermissionResult.GRANTED -> requiresPermission.get()?.invoke()
                PermissionResult.DENIED -> onPermissionDenied?.get()?.invoke()
                PermissionResult.DENIED_AND_DISABLED -> onNeverAskAgain?.get()?.invoke()
                else -> Unit
            }
        })
    }

    fun removeObservers(owner: LifecycleOwner) = permissionRequestResult.removeObservers(owner)

    private fun <T> MutableLiveData<T>.notifyObserver() {
        this.value = this.value
    }
}


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/PermissionsRequester.kt
================================================
package permissions.dispatcher.ktx

/**
 * An intermediate class that is able to launch permissions request process as appropriate.
 * [launch] method kicks off the actual process.
 */
interface PermissionsRequester {
    fun launch()
}


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/PermissionsRequesterImpl.kt
================================================
package permissions.dispatcher.ktx

import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelProvider
import permissions.dispatcher.PermissionUtils.shouldShowRequestPermissionRationale
import java.lang.ref.WeakReference

internal class PermissionsRequesterImpl(
    private val permissions: Array<out String>,
    private val activity: FragmentActivity,
    private val onShowRationale: ShowRationaleFun?,
    private val onPermissionDenied: Fun?,
    private val requiresPermission: Fun,
    onNeverAskAgain: Fun?,
    private val permissionRequestType: PermissionRequestType
) : PermissionsRequester {
    private val viewModel = ViewModelProvider(activity).get(PermissionRequestViewModel::class.java)
    private val requestFun: Fun = {
        activity.supportFragmentManager
            .beginTransaction()
            .replace(android.R.id.content, permissionRequestType.fragment(permissions))
            .commitAllowingStateLoss()
    }

    init {
        viewModel.observe(
            activity,
            // https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/729
            permissions.sortedArray().contentToString(),
            WeakReference(requiresPermission),
            WeakReference(onPermissionDenied),
            WeakReference(onNeverAskAgain)
        )
    }

    override fun launch() {
        if (permissionRequestType.checkPermissions(activity, permissions)) {
            viewModel.removeObservers(activity)
            requiresPermission()
        } else {
            if (shouldShowRequestPermissionRationale(activity, *permissions) && onShowRationale != null) {
                onShowRationale.invoke(KtxPermissionRequest.create(onPermissionDenied, requestFun))
            } else {
                requestFun.invoke()
            }
        }
    }
}


================================================
FILE: ktx/src/main/java/permissions/dispatcher/ktx/TypeAliases.kt
================================================
package permissions.dispatcher.ktx

import permissions.dispatcher.PermissionRequest

internal typealias Fun = () -> Unit
internal typealias ShowRationaleFun = (PermissionRequest) -> Unit


================================================
FILE: ktx/src/test/java/permissions/dispatcher/test/EventTest.kt
================================================
package permissions.dispatcher.test

import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNull
import org.junit.Test
import permissions.dispatcher.ktx.Event

class EventTest {
    @Test
    fun `getContentIfNotHandled returns null from the second access`() {
        val testInstance = Event(1)
        assertEquals(1, testInstance.getContentIfNotHandled())
        assertNull(testInstance.getContentIfNotHandled())
        assertNull(testInstance.getContentIfNotHandled())
    }
}


================================================
FILE: ktx/src/test/java/permissions/dispatcher/test/KtxPermissionRequestTest.kt
================================================
package permissions.dispatcher.test

import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.never
import permissions.dispatcher.ktx.*

class KtxPermissionRequestTest {
    private lateinit var onPermissionDenied: Fun
    private lateinit var requiresPermission: Fun

    @Before
    fun setUp() {
        onPermissionDenied = mock()
        requiresPermission = mock()
    }

    @Test
    fun `PermissionRequest#proceed invokes requiresPermission`() {
        val request = KtxPermissionRequest.create(onPermissionDenied, requiresPermission)
        request.proceed()

        verify(onPermissionDenied, never()).invoke()
        verify(requiresPermission).invoke()
    }

    @Test
    fun `PermissionRequest#cancel invokes onPermissionDenied`() {
        val request = KtxPermissionRequest.create(onPermissionDenied, requiresPermission)
        request.cancel()

        verify(onPermissionDenied).invoke()
        verify(requiresPermission, never()).invoke()
    }
}


================================================
FILE: ktx/src/test/java/permissions/dispatcher/test/LocationPermissionTest.kt
================================================
package permissions.dispatcher.test

import org.junit.Assert.*
import org.junit.Test
import permissions.dispatcher.ktx.LocationPermission
import permissions.dispatcher.ktx.filterByApiLevel

class LocationPermissionTest {
    @Test
    fun `BACKGRGROUND should be excluded when sdkVer is less than 29`() {
        val permissions = LocationPermission.values()
            .filterByApiLevel(sdkVer = LocationPermission.BACKGROUND.apiLevel - 1)
        assertEquals(2, permissions.size)
        assertFalse(permissions.contains(LocationPermission.BACKGROUND.permission))
    }

    @Test
    fun `BACKGRGROUND should be included when sdkVer equals to 29`() {
        val permissions = LocationPermission.values()
            .filterByApiLevel(sdkVer = LocationPermission.BACKGROUND.apiLevel)
        assertEquals(3, permissions.size)
        assertTrue(permissions.contains(LocationPermission.BACKGROUND.permission))
    }

    @Test
    fun `BACKGRGROUND should be included when sdkVer is more than 29`() {
        val permissions = LocationPermission.values()
            .filterByApiLevel(sdkVer = LocationPermission.BACKGROUND.apiLevel + 1)
        assertEquals(3, permissions.size)
        assertTrue(permissions.contains(LocationPermission.BACKGROUND.permission))
    }
}


================================================
FILE: ktx/src/test/java/permissions/dispatcher/test/PermissionRequestViewModelTest.kt
================================================
package permissions.dispatcher.test

import android.Manifest
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.never
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import permissions.dispatcher.ktx.Fun
import permissions.dispatcher.ktx.PermissionRequestViewModel
import permissions.dispatcher.ktx.PermissionResult
import java.lang.ref.WeakReference

class PermissionRequestViewModelTest {
    @Rule
    @JvmField
    val instantTaskExecutorRule = InstantTaskExecutorRule()
    private val permission = arrayOf(Manifest.permission.CAMERA).contentToString()
    private lateinit var viewModel: PermissionRequestViewModel
    private lateinit var lifecycleOwner: LifecycleOwner
    private lateinit var requiresPermission: Fun
    private lateinit var onPermissionDenied: Fun
    private lateinit var onNeverAskAgain: Fun

    @Before
    fun setup() {
        viewModel = PermissionRequestViewModel()

        lifecycleOwner = mock()
        val lifecycle = LifecycleRegistry(mock())
        lifecycle.markState(Lifecycle.State.RESUMED)
        whenever(lifecycleOwner.lifecycle).thenReturn(lifecycle)

        onPermissionDenied = mock()
        requiresPermission = mock()
        onNeverAskAgain = mock()
    }

    @After
    fun cleanup() {
        viewModel.removeObservers(lifecycleOwner)
    }

    @Test
    fun `GRANTED emits requiresPermission`() {
        viewModel.observe(
            lifecycleOwner,
            permission,
            WeakReference(requiresPermission),
            WeakReference(onPermissionDenied),
            WeakReference(onNeverAskAgain)
        )
        viewModel.postPermissionRequestResult(permission, PermissionResult.GRANTED)

        verify(requiresPermission).invoke()
        verify(onPermissionDenied, never()).invoke()
        verify(onNeverAskAgain, never()).invoke()
    }

    @Test
    fun `DENIED emits onPermissionDenied`() {
        viewModel.observe(
            lifecycleOwner,
            permission,
            WeakReference(requiresPermission),
            WeakReference(onPermissionDenied),
            WeakReference(onNeverAskAgain)
        )
        viewModel.postPermissionRequestResult(permission, PermissionResult.DENIED)

        verify(requiresPermission, never()).invoke()
        verify(onPermissionDenied).invoke()
        verify(onNeverAskAgain, never()).invoke()
    }

    @Test
    fun `DENIED_AND_DISABLED emits onNeverAskAgain`() {
        viewModel.observe(
            lifecycleOwner,
            permission,
            WeakReference(requiresPermission),
            WeakReference(onPermissionDenied),
            WeakReference(onNeverAskAgain)
        )
        viewModel.postPermissionRequestResult(permission, PermissionResult.DENIED_AND_DISABLED)

        verify(requiresPermission, never()).invoke()
        verify(onPermissionDenied, never()).invoke()
        verify(onNeverAskAgain).invoke()
    }
}


================================================
FILE: ktx/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
================================================
mock-maker-inline

================================================
FILE: ktx-sample/.gitignore
================================================
/build


================================================
FILE: ktx-sample/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion COMPILE_SDK_VERSION
    defaultConfig {
        applicationId "permissions.dispatcher.sample"
        targetSdkVersion TARGET_SDK_VERSION
        minSdkVersion SAMPLE_MIN_SDK_VERSION
        versionCode 1
        versionName "1.0"
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$KOTLIN_VERSION"
    implementation "androidx.appcompat:appcompat:$ANDROIDX_LIBRARY_VERSION"
    implementation project(':ktx')
}


================================================
FILE: ktx-sample/src/main/AndroidManifest.xml
================================================
<?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="permissions.dispatcher.ktx.sample">

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


================================================
FILE: ktx-sample/src/main/java/permissions/dispatcher/ktx/sample/MainActivity.kt
================================================
package permissions.dispatcher.ktx.sample

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (savedInstanceState == null) {
            val fragment = MainFragment()
            supportFragmentManager.beginTransaction()
                .replace(R.id.container, fragment)
                .commitNowAllowingStateLoss()
        }
    }
}


================================================
FILE: ktx-sample/src/main/java/permissions/dispatcher/ktx/sample/MainFragment.kt
================================================
package permissions.dispatcher.ktx.sample

import android.Manifest
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import permissions.dispatcher.PermissionRequest
import permissions.dispatcher.ktx.PermissionsRequester
import permissions.dispatcher.ktx.constructPermissionsRequest

class MainFragment : Fragment() {
    private lateinit var permissionsRequester: PermissionsRequester
    private lateinit var fileManagerRequester: PermissionsRequester

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        permissionsRequester = constructPermissionsRequest(
            Manifest.permission.CAMERA,
            onShowRationale = ::onCameraShowRationale,
            onPermissionDenied = ::onCameraDenied,
            onNeverAskAgain = ::onCameraNeverAskAgain,
            requiresPermission = ::openCamera
        )
        fileManagerRequester = constructPermissionsRequest(
            Manifest.permission.READ_EXTERNAL_STORAGE,
            requiresPermission = ::openFileManager
        )
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? = inflater.inflate(R.layout.fragment_main, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<Button>(R.id.button_camera).setOnClickListener {
            permissionsRequester.launch()
        }
        view.findViewById<Button>(R.id.button_file_manager).setOnClickListener {
            fileManagerRequester.launch()
        }
    }

    private fun openCamera() {
        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(MediaStore.EXTRA_OUTPUT, Environment.DIRECTORY_DOWNLOADS + "/temp.png")
        startActivityForResult(intent, 1)
    }

    private fun openFileManager() {
        val intent = Intent().setAction(Intent.ACTION_GET_CONTENT)
        startActivityForResult(Intent.createChooser(intent, "Choose file"), 2)
    }

    private fun onCameraDenied() {
        Toast.makeText(requireContext(), R.string.permission_camera_denied, Toast.LENGTH_SHORT).show()
    }

    private fun onCameraShowRationale(request: PermissionRequest) {
        // NOTE: Show a rationale to explain why the permission is needed, e.g. with a dialog.
        // Call proceed() or cancel() on the provided PermissionRequest to continue or abort
        showRationaleDialog(R.string.permission_camera_rationale, request)
    }

    private fun onCameraNeverAskAgain() {
        Toast.makeText(requireContext(), R.string.permission_camera_never_ask_again, Toast.LENGTH_SHORT).show()
    }

    private fun showRationaleDialog(@StringRes messageResId: Int, request: PermissionRequest) {
        AlertDialog.Builder(requireContext())
            .setPositiveButton(R.string.button_allow) { _, _ -> request.proceed() }
            .setNegativeButton(R.string.button_deny) { _, _ -> request.cancel() }
            .setCancelable(false)
            .setMessage(messageResId)
            .show()
    }
}


================================================
FILE: ktx-sample/src/main/res/layout/activity_main.xml
================================================
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" />


================================================
FILE: ktx-sample/src/main/res/layout/fragment_camera.xml
================================================
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="@string/back" />

    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

================================================
FILE: ktx-sample/src/main/res/layout/fragment_main.xml
================================================
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/vertical_page_margin"
    android:paddingLeft="@dimen/horizontal_page_margin"
    android:paddingRight="@dimen/horizontal_page_margin"
    android:paddingTop="@dimen/vertical_page_margin"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button_camera"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/show_camera" />

    <Button
        android:id="@+id/button_file_manager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/open_file_manager" />

</LinearLayout>


================================================
FILE: ktx-sample/src/main/res/values/dimens.xml
================================================
<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="margin_medium">16dp</dimen>
    <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
    <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
</resources>


================================================
FILE: ktx-sample/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">PermissionsDispatcher Sample</string>
    <string name="back">Back</string>
    <string name="button_allow">Allow</string>
    <string name="button_deny">Deny</string>

    <string name="contacts_string">Total number of contacts: %1$d\nFirst contact:<b>%2$s</b></string>
    <string name="contacts_empty">Contacts not loaded.</string>

    <string name="show_camera">Show camera preview</string>
    <string name="open_file_manager">Open file manager</string>

    <string name="permission_camera_rationale">Camera permission is needed to show the camera preview.</string>
    <string name="permission_camera_denied">Camera permission was denied. Please consider granting it in order to access the camera!</string>
    <string name="permission_camera_never_ask_again">Camera permission was denied with never ask again.</string>
</resources>


================================================
FILE: ktx-sample/src/main/res/values/template-styles.xml
================================================
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar" />
</resources>


================================================
FILE: library/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: "com.vanniktech.maven.publish"

android {
    compileSdkVersion COMPILE_SDK_VERSION
    defaultConfig {
        minSdkVersion MIN_SDK_VERSION
        targetSdkVersion TARGET_SDK_VERSION
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_6
        targetCompatibility JavaVersion.VERSION_1_6
    }

    packagingOptions {
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }

    libraryVariants.all {
        it.generateBuildConfigProvider.configure { enabled = false }
    }
}

configurations {
    lintChecks
}

dependencies {
    implementation "androidx.core:core:$ANDROIDX_LIBRARY_VERSION"
    implementation "androidx.fragment:fragment:$ANDROIDX_LIBRARY_VERSION"
    implementation "androidx.collection:collection:$ANDROIDX_LIBRARY_VERSION"

    testImplementation "junit:junit:$JUNIT_VERSION"
    testImplementation "org.mockito:mockito-core:$MOCKITO_VERSION"
    testImplementation "org.powermock:powermock-api-mockito2:$POWERMOCK_VERSION"
    testImplementation "org.powermock:powermock-module-junit4:$POWERMOCK_VERSION"

    api project(path: ':annotation')
    lintChecks project(path: ':lint', configuration: 'lintChecks')
}

task copyLintJar(type: Copy) {
    from(configurations.lintChecks) {
        rename { 'lint.jar' }
    }
    into 'build/intermediates/lint/'
}

project.afterEvaluate {
    def compileLintTask = project.tasks.find { it.name == 'compileLint' }
    compileLintTask.dependsOn(copyLintJar)
}


================================================
FILE: library/gradle.properties
================================================
POM_NAME        = permissionsdispatcher
POM_ARTIFACT_ID = permissionsdispatcher
POM_PACKAGING   = aar


================================================
FILE: library/src/main/AndroidManifest.xml
================================================
<manifest package="permissions.dispatcher"/>


================================================
FILE: library/src/main/java/permissions/dispatcher/PermissionUtils.java
================================================
package permissions.dispatcher;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.collection.SimpleArrayMap;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;

public final class PermissionUtils {
    // Map of dangerous permissions introduced in later framework versions.
    // Used to conditionally bypass permission-hold checks on older devices.
    // ref: https://developer.android.com/reference/android/Manifest.permission
    private static final SimpleArrayMap<String, Integer> MIN_SDK_PERMISSIONS;

    static {
        MIN_SDK_PERMISSIONS = new SimpleArrayMap<String, Integer>(13);
        MIN_SDK_PERMISSIONS.put("com.android.voicemail.permission.ADD_VOICEMAIL", 14);
        MIN_SDK_PERMISSIONS.put("android.permission.READ_CALL_LOG", 16);
        MIN_SDK_PERMISSIONS.put("android.permission.READ_EXTERNAL_STORAGE", 16);
        MIN_SDK_PERMISSIONS.put("android.permission.WRITE_CALL_LOG", 16);
        MIN_SDK_PERMISSIONS.put("android.permission.BODY_SENSORS", 20);
        MIN_SDK_PERMISSIONS.put("android.permission.SYSTEM_ALERT_WINDOW", 23);
        MIN_SDK_PERMISSIONS.put("android.permission.WRITE_SETTINGS", 23);
        MIN_SDK_PERMISSIONS.put("android.permission.READ_PHONE_NUMBERS", 26);
        MIN_SDK_PERMISSIONS.put("android.permission.ANSWER_PHONE_CALLS", 26);
        MIN_SDK_PERMISSIONS.put("android.permission.ACCEPT_HANDOVER", 28);
        MIN_SDK_PERMISSIONS.put("android.permission.ACTIVITY_RECOGNITION", 29);
        MIN_SDK_PERMISSIONS.put("android.permission.ACCESS_MEDIA_LOCATION", 29);
        MIN_SDK_PERMISSIONS.put("android.permission.ACCESS_BACKGROUND_LOCATION", 29);
    }

    private PermissionUtils() {
    }

    /**
     * Checks all given permissions have been granted.
     *
     * @param grantResults results
     * @return returns true if all permissions have been granted.
     */
    public static boolean verifyPermissions(int... grantResults) {
        if (grantResults.length == 0) {
            return false;
        }
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns true if the permission exists in this SDK version
     *
     * @param permission permission
     * @return returns true if the permission exists in this SDK version
     */
    private static boolean permissionExists(String permission) {
        // Check if the permission could potentially be missing on this device
        Integer minVersion = MIN_SDK_PERMISSIONS.get(permission);
        // If null was returned from the above call, there is no need for a device API level check for the permission;
        // otherwise, we check if its minimum API level requirement is met
        return minVersion == null || Build.VERSION.SDK_INT >= minVersion;
    }

    /**
     * Returns true if the Activity or Fragment has access to all given permissions.
     *
     * @param context     context
     * @param permissions permission list
     * @return returns true if the Activity or Fragment has access to all given permissions.
     */
    public static boolean hasSelfPermissions(Context context, String... permissions) {
        for (String permission : permissions) {
            if (permissionExists(permission) && !hasSelfPermission(context, permission)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Determine context has access to the given permission.
     * <p>
     * This is a workaround for RuntimeException of Parcel#readException.
     * For more detail, check this issue https://github.com/hotchemi/PermissionsDispatcher/issues/107
     *
     * @param context    context
     * @param permission permission
     * @return true if context has access to the given permission, false otherwise.
     * @see #hasSelfPermissions(Context, String...)
     */
    private static boolean hasSelfPermission(Context context, String permission) {
        try {
            return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
        } catch (RuntimeException t) {
            return false;
        }
    }

    /**
     * Checks given permissions are needed to show rationale.
     *
     * @param activity    activity
     * @param permissions permission list
     * @return returns true if one of the permission is needed to show rationale.
     */
    public static boolean shouldShowRequestPermissionRationale(Activity activity, String... permissions) {
        for (String permission : permissions) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks given permissions are needed to show rationale.
     *
     * @param fragment    fragment
     * @param permissions permission list
     * @return returns true if one of the permission is needed to show rationale.
     */
    public static boolean shouldShowRequestPermissionRationale(Fragment fragment, String... permissions) {
        for (String permission : permissions) {
            if (fragment.shouldShowRequestPermissionRationale(permission)) {
                return true;
            }
        }
        return false;
    }
}


================================================
FILE: library/src/test/java/permissions/dispatcher/ApiLevelTestSuite.java
================================================
package permissions.dispatcher;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.BDDMockito;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import androidx.core.content.PermissionChecker;

import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
import static android.os.Build.VERSION_CODES.JELLY_BEAN;
import static android.os.Build.VERSION_CODES.KITKAT_WATCH;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;

/**
 * Test suite related to permissions that were added to Android at a later point in the platform's lifecycle.
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest({PermissionChecker.class})
@SuppressLint("NewApi")
public class ApiLevelTestSuite {

    private static final int MOST_RECENT_API_LEVEL = Build.VERSION_CODES.M;
    private static final int NEEDS_PERMISSION_CHECK = 1024;

    private final Context mockContext;

    public ApiLevelTestSuite() {
        // Mock out Context
        mockContext = Mockito.mock(Context.class);
    }

    @Before
    public void beforeTest() throws Exception {
        // Reset the API level assumption
        this.resetApiLevel();

        // Mock out PermissionChecker, so that "checkSelfPermission"
        // always returns NEEDS_PERMISSION_CHECK.
        // This way, we can distinguish between auto-grants and permission-checks
        PowerMockito.mockStatic(PermissionChecker.class);
        BDDMockito.given(PermissionChecker.checkSelfPermission(any(Context.class), anyString())).willReturn(NEEDS_PERMISSION_CHECK);
    }

    @Test
    public void testAssumeApiLevelWorking() throws Exception {
        // Check that manually setting the API level to a value works
        assumeApiLevel(ICE_CREAM_SANDWICH);
        assertEquals(ICE_CREAM_SANDWICH, Build.VERSION.SDK_INT);

        // Check that resetting the API level works
        resetApiLevel();
        assertEquals(0, Build.VERSION.SDK_INT);
    }

    @Test
    public void testCheckSelfPermissionMockWorking() throws Exception {
        // Check that mocking out PermissionChecker works
        assertEquals(NEEDS_PERMISSION_CHECK, PermissionChecker.checkSelfPermission(mockContext, "permission"));
    }

    @Test
    public void testAddVoicemailPermission() throws Exception {
        // ADD_VOICEMAIL:
        // Added in API level 14 ("Ice Cream Sandwich")
        iteratePermissionCheck(Manifest.permission.ADD_VOICEMAIL, ICE_CREAM_SANDWICH);
    }

    @Test
    public void testBodySensorsPermission() throws Exception {
        // BODY_SENSORS:
        // Added in API level 20 ("KitKat Watch")
        iteratePermissionCheck(Manifest.permission.BODY_SENSORS, KITKAT_WATCH);
    }

    @Test
    public void testReadCallLogPermission() throws Exception {
        // READ_CALL_LOG:
        // Added in API level 16 ("Jelly Bean")
        iteratePermissionCheck(Manifest.permission.READ_CALL_LOG, JELLY_BEAN);
    }

    @Test
    public void testReadExternalStoragePermission() throws Exception {
        // READ_EXTERNAL_STORAGE:
        // Added in API level 16 ("Jelly Bean")
        iteratePermissionCheck(Manifest.permission.READ_EXTERNAL_STORAGE, JELLY_BEAN);
    }

    @Test
    public void testWriteCallLogPermission() throws Exception {
        // WRITE_CALL_LOG:
        // Added in API level 16 ("Jelly Bean")
        iteratePermissionCheck(Manifest.permission.WRITE_CALL_LOG, JELLY_BEAN);
    }

    @Test
    public void testBodySensors() throws Exception {
        iteratePermissionCheck(Manifest.permission.BODY_SENSORS, KITKAT_WATCH);
    }

    @Test
    public void testReadPhoneNumbers() throws Exception {
        iteratePermissionCheck(Manifest.permission.READ_PHONE_NUMBERS, O);
    }

    @Test
    public void testAnswerPhoneNumbers() throws Exception {
        iteratePermissionCheck(Manifest.permission.ANSWER_PHONE_CALLS, O);
    }

    @Test
    public void testAcceptHandOver() throws Exception {
        iteratePermissionCheck(Manifest.permission.ACCEPT_HANDOVER, P);
    }

    @Test
    public void testActivityRecognition() throws Exception {
        iteratePermissionCheck(Manifest.permission.ACTIVITY_RECOGNITION, Q);
    }

    @Test
    public void testAccessMediaLocation() throws Exception {
        iteratePermissionCheck(Manifest.permission.ACCESS_MEDIA_LOCATION, Q);
    }

    @Test
    public void testAccessBackgroundLocation() throws Exception {
        iteratePermissionCheck(Manifest.permission.ACCESS_BACKGROUND_LOCATION, Q);
    }

    /* Begin private */

    private void iteratePermissionCheck(String permission, int permissionMinLevel) throws Exception {
        for (int apiLevel = 0; apiLevel <= MOST_RECENT_API_LEVEL; apiLevel++) {
            // Adjust the current API level
            assumeApiLevel(apiLevel);

            // Iterate over all API levels and verify that the permission is auto-granted
            // below the minimum available level. For all other API levels, the permission
            // shouldn't be auto-granted.
            boolean shouldAutoGrantPermission = apiLevel < permissionMinLevel;
            boolean hasPermission = PermissionUtils.hasSelfPermissions(mockContext, permission);

            if (shouldAutoGrantPermission != hasPermission) {
                // Mismatch, because the permission shouldn't be granted because the API level requires a check,
                // OR the permission should be granted because it doesn't exist on the current API level
                throw new AssertionError(permission + " check on API level " + apiLevel + " shouldn't return auto-grant=" + shouldAutoGrantPermission + " amd has-permission=" + hasPermission);
            }
        }
    }

    private void assumeApiLevel(int apiLevel) throws Exception {
        // Adjust the value of Build.VERSION.SDK_INT statically using reflection
        Field sdkIntField = Build.VERSION.class.getDeclaredField("SDK_INT");
        sdkIntField.setAccessible(true);

        // Temporarily remove the SDK_INT's "final" modifier
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(sdkIntField, sdkIntField.getModifiers() & ~Modifier.FINAL);

        // Update the SDK_INT value, re-finalize the field, and lock it again
        sdkIntField.set(null, apiLevel);
        modifiersField.setInt(sdkIntField, sdkIntField.getModifiers() | Modifier.FINAL);
        sdkIntField.setAccessible(false);
    }

    private void resetApiLevel() throws Exception {
        this.assumeApiLevel(0);
    }
}

================================================
FILE: lint/.gitignore
================================================
/build


================================================
FILE: lint/build.gradle
================================================
apply plugin: 'java'
apply plugin: 'kotlin'

targetCompatibility = JavaVersion.VERSION_1_6
sourceCompatibility = JavaVersion.VERSION_1_6

configurations {
    lintChecks
}

dependencies {
    implementation "com.android.tools.lint:lint-api:$LINT_VERSION"
    implementation "com.android.tools.lint:lint-checks:$LINT_VERSION"
    implementation "com.android.tools.lint:lint:$LINT_VERSION"

    implementation "androidx.core:core:$ANDROIDX_LIBRARY_VERSION"
    implementation "androidx.fragment:fragment:$ANDROIDX_LIBRARY_VERSION"

    testImplementation "junit:junit:$JUNIT_VERSION"
    testImplementation "com.android.tools.lint:lint-tests:$LINT_VERSION"
    testImplementation "com.android.tools:testutils:$LINT_VERSION"

    lintChecks files(jar)
}

jar {
    manifest {
        attributes("Lint-Registry-v2": "permissions.dispatcher.PermissionsDispatcherIssueRegistry")
    }
}


================================================
FILE: lint/src/main/java/permissions/dispatcher/CallNeedsPermissionDetector.java
================================================
package permissions.dispatcher;

import com.android.tools.lint.client.api.UElementHandler;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.uast.UAnnotation;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UFile;
import org.jetbrains.uast.UMethod;
import org.jetbrains.uast.visitor.AbstractUastVisitor;

import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public final class CallNeedsPermissionDetector extends Detector implements Detector.UastScanner {

    private static final String COLON = ":";

    static final Issue ISSUE = Issue.create("CallNeedsPermission",
            "Call the corresponding \"WithPermissionCheck\" method of the generated PermissionsDispatcher class instead",
            "Directly invoking a method annotated with @NeedsPermission may lead to misleading behaviour on devices running Marshmallow and up. Therefore, it is advised to use the generated PermissionsDispatcher class instead, which provides a \"WithPermissionCheck\" method that safely handles runtime permissions.",
            Category.CORRECTNESS,
            7,
            Severity.ERROR,
            new Implementation(CallNeedsPermissionDetector.class, EnumSet.of(Scope.ALL_JAVA_FILES)));

    private static Set<String> annotatedMethods = new HashSet<String>();

    @Override
    public List<Class<? extends UElement>> getApplicableUastTypes() {
        return Collections.<Class<? extends UElement>>singletonList(UClass.class);
    }

    @Override
    public UElementHandler createUastHandler(@NotNull final JavaContext context) {
        return new UElementHandler() {
            @Override
            public void visitClass(@NotNull UClass node) {
                node.accept(new AnnotationChecker(context));
            }
        };
    }

    private static class AnnotationChecker extends AbstractUastVisitor {

        private final JavaContext context;

        private boolean hasRuntimePermissionAnnotation;

        private AnnotationChecker(JavaContext context) {
            this.context = context;
        }

        @Override
        public boolean visitAnnotation(@NotNull UAnnotation node) {
            if (!"permissions.dispatcher.RuntimePermissions".equals(node.getQualifiedName())) {
                return true;
            }
            hasRuntimePermissionAnnotation = true;
            return true;
        }

        @Override
        public boolean visitCallExpression(@NotNull UCallExpression node) {
            if (isGeneratedFiles(context) || !hasRuntimePermissionAnnotation) {
                return true;
            }
            if (node.getReceiver() == null && annotatedMethods.contains(methodIdentifier(node))) {
                context.report(ISSUE, node, context.getLocation(node), "Trying to access permission-protected method directly");
            }
            return true;
        }

        /**
         * Generate method identifier from method information.
         *
         * @param node UCallExpression
         * @return className + methodName + parametersType
         */
        @Nullable
        private static String methodIdentifier(@NotNull UCallExpression node) {
            UElement element = node.getUastParent();
            while (element != null) {
                if (element instanceof UClass) {
                    break;
                }
                element = element.getUastParent();
            }
            UClass uClass = (UClass) element;
            if (uClass == null || node.getMethodName() == null) {
                return null;
            }
            return uClass.getName() + COLON + node.getMethodName();
        }

        @Override
        public boolean visitMethod(@NotNull UMethod node) {
            if (isGeneratedFiles(context)) {
                return super.visitMethod(node);
            }
            UAnnotation annotation = node.findAnnotation("permissions.dispatcher.NeedsPermission");
            if (annotation == null) {
                return super.visitMethod(node);
            }
            String methodIdentifier = methodIdentifier(node);
            if (methodIdentifier == null) {
                return super.visitMethod(node);
            }
            annotatedMethods.add(methodIdentifier);
            return super.visitMethod(node);
        }

        /**
         * Generate method identifier from method information.
         *
         * @param node UMethod
         * @return className + methodName + parametersType
         */
        @Nullable
        private static String methodIdentifier(@NotNull UMethod node) {
            UElement parent = node.getUastParent();
            if (!(parent instanceof UClass)) {
                return null;
            }
            UClass uClass = (UClass) parent;
            return uClass.getName() + COLON + node.getName();
        }

        private static boolean isGeneratedFiles(JavaContext context) {
            UFile sourceFile = context.getUastFile();
            if (sourceFile == null) {
                return false;
            }
            List<UClass> classes = sourceFile.getClasses();
            if (classes.isEmpty()) {
                return false;
            }
            String qualifiedName = classes.get(0).getName();
            return qualifiedName != null && qualifiedName.contains("PermissionsDispatcher");
        }
    }
}


================================================
FILE: lint/src/main/java/permissions/dispatcher/CallOnRequestPermissionsResultDetector.java
================================================
package permissions.dispatcher;

import androidx.annotation.Nullable;

import com.android.tools.lint.client.api.UElementHandler;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.intellij.psi.PsiElement;

import org.jetbrains.uast.UAnnotation;
import org.jetbrains.uast.UBlockExpression;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UExpression;
import org.jetbrains.uast.UMethod;
import org.jetbrains.uast.UQualifiedReferenceExpression;
import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression;
import org.jetbrains.uast.kotlin.KotlinUImplicitReturnExpression;
import org.jetbrains.uast.kotlin.KotlinUQualifiedReferenceExpression;
import org.jetbrains.uast.visitor.AbstractUastVisitor;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;

public final class CallOnRequestPermissionsResultDetector extends Detector implements Detector.UastScanner {

    static final Issue ISSUE = Issue.create("NeedOnRequestPermissionsResult",
            "Call the \"onRequestPermissionsResult\" method of the generated PermissionsDispatcher class in the respective method of your Activity or Fragment",
            "You are required to inform the generated PermissionsDispatcher class about the results of a permission request. In your class annotated with @RuntimePermissions, override the \"onRequestPermissionsResult\" method and call through to the generated PermissionsDispatcher method with the same name.",
            Category.CORRECTNESS,
            5,
            Severity.ERROR,
            new Implementation(CallOnRequestPermissionsResultDetector.class,
                    EnumSet.of(Scope.JAVA_FILE)));

    @Override
    public List<Class<? extends UElement>> getApplicableUastTypes() {
        return Collections.<Class<? extends UElement>>singletonList(UClass.class);
    }

    @Override
    public UElementHandler createUastHandler(final JavaContext context) {
        return new UElementHandler() {
            @Override
            public void visitClass(UClass node) {
                node.accept(new OnRequestPermissionsResultChecker(context, node));
            }
        };
    }

    private static class OnRequestPermissionsResultChecker extends AbstractUastVisitor {

        private final JavaContext context;

        private final String className;

        private final boolean isKotlin;

        private boolean hasRuntimePermissionAnnotation;


        private OnRequestPermissionsResultChecker(JavaContext context, UClass klass) {
            this.context = context;
            this.className = klass.getName();
            isKotlin = context.getPsiFile() != null && "kotlin".equals(context.getPsiFile().getLanguage().getID());
        }

        @Override
        public boolean visitAnnotation(UAnnotation node) {
            if (!"permissions.dispatcher.RuntimePermissions".equals(node.getQualifiedName())) {
                return true;
            }
            hasRuntimePermissionAnnotation = true;
            return true;
        }

        @Override
        public boolean visitMethod(UMethod node) {
            if (!hasRuntimePermissionAnnotation) {
                return true;
            }
            if (!"onRequestPermissionsResult".equals(node.getName())) {
                return true;
            }
            if (hasRuntimePermissionAnnotation && !isGeneratedMethodCalled(node, className, isKotlin)) {
                context.report(ISSUE, context.getLocation(node), "Generated onRequestPermissionsResult method not called");
            }
            return true;
        }

        private static boolean assertMethodName(@Nullable String name) {
            return "onRequestPermissionsResult".equals(name);
        }

        private static boolean isGeneratedMethodCalled(UMethod method, String className, boolean isKotlin) {
            UExpression methodBody = method.getUastBody();
            if (methodBody == null) {
                return false;
            }
            if (methodBody instanceof UBlockExpression) {
                UBlockExpression methodBodyExpression = (UBlockExpression) methodBody;
                List<UExpression> expressions = methodBodyExpression.getExpressions();
                for (UExpression expression : expressions) {
                    if (isKotlin && expression instanceof KotlinUFunctionCallExpression) {
                        KotlinUFunctionCallExpression functionalExpression = (KotlinUFunctionCallExpression) expression;
                        return assertMethodName(functionalExpression.getMethodName());
                    } else if (isKotlin && expression instanceof KotlinUImplicitReturnExpression) {
                        KotlinUImplicitReturnExpression returnExpression =
                                (KotlinUImplicitReturnExpression) expression;
                        UExpression uExpression = returnExpression.returnExpression;
                        if (uExpression instanceof KotlinUFunctionCallExpression) {
                            KotlinUFunctionCallExpression uFunctionCallExpression =
                                    (KotlinUFunctionCallExpression) uExpression;
                            return assertMethodName(uFunctionCallExpression.getMethodName());
                        }
                    }

                    if (!(expression instanceof UQualifiedReferenceExpression)) {
                        continue;
                    }

                    UQualifiedReferenceExpression referenceExpression = (UQualifiedReferenceExpression) expression;
                    UExpression receiverExpression = referenceExpression.getReceiver();
                    PsiElement receiverPsi = receiverExpression.getPsi();
                    if (receiverPsi == null) {
                        continue; // can this case be happened?
                    }
                    String receiverName = receiverPsi.getText();
                    if ("super".equals(receiverName)) {
                        // skip super method call
                        continue;
                    }

                    if (isKotlin && referenceExpression instanceof KotlinUQualifiedReferenceExpression) {
                        return assertMethodName(referenceExpression.getResolvedName());
                    } else {
                        String targetClassName = className + "PermissionsDispatcher";
                        if (targetClassName.equals(receiverName) && assertMethodName(referenceExpression.getResolvedName())) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    }
}


================================================
FILE: lint/src/main/java/permissions/dispatcher/NoCorrespondingNeedsPermissionDetector.java
================================================
package permissions.dispatcher;

import com.android.tools.lint.client.api.UElementHandler;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;

import org.jetbrains.uast.UAnnotation;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UNamedExpression;
import org.jetbrains.uast.visitor.AbstractUastVisitor;

import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public final class NoCorrespondingNeedsPermissionDetector extends Detector implements Detector.UastScanner {

    static final Issue ISSUE = Issue.create("NoCorrespondingNeedsPermission",
            "The method annotated with @OnShowRationale has no corresponding @NeedsPermission method, and will therefore be ignored by PermissionsDispatcher",
            "The @OnShowRationale method with a certain signature is internally connected to another method annotated with @NeedsPermission and the same annotation value. Please ensure that there is a @NeedsPermission method with matching annotation values for this method.",
            Category.CORRECTNESS,
            4,
            Severity.ERROR,
            new Implementation(NoCorrespondingNeedsPermissionDetector.class,
                    EnumSet.of(Scope.JAVA_FILE)));

    @Override
    public List<Class<? extends UElement>> getApplicableUastTypes() {
        return Collections.<Class<? extends UElement>>singletonList(UClass.class);
    }

    @Override
    public UElementHandler createUastHandler(final JavaContext context) {
        return new UElementHandler() {
            @Override public void visitClass(UClass node) {
                node.accept(new AnnotationChecker(context));
            }
        };
    }

    private static class AnnotationChecker extends AbstractUastVisitor {

        private final JavaContext context;

        private final Set<UAnnotation> needsPermissionAnnotations;

        private final Set<UAnnotation> onShowRationaleAnnotations;

        private AnnotationChecker(JavaContext context) {
            this.context = context;
            needsPermissionAnnotations = new HashSet<UAnnotation>();
            onShowRationaleAnnotations = new HashSet<UAnnotation>();
        }

        @Override
        public boolean visitAnnotation(UAnnotation node) {
            if (!context.isEnabled(ISSUE)) {
                return true;
            }
            // Let's store NeedsPermission and OnShowRationale
            String type = node.getQualifiedName();
            if ("permissions.dispatcher.NeedsPermission".equals(type)) {
                needsPermissionAnnotations.add(node);
            } else if ("permissions.dispatcher.OnShowRationale".equals(type)) {
                onShowRationaleAnnotations.add(node);
            }
            if (onShowRationaleAnnotations.isEmpty()) {
                return true;
            }
            return true;
        }

        @Override
        public void afterVisitClass(UClass node) {
            // If there is OnShowRationale, find corresponding NeedsPermission
            for (UAnnotation onShowRationaleAnnotation : onShowRationaleAnnotations) {
                boolean found = false;
                for (UAnnotation needsPermissionAnnotation : needsPermissionAnnotations) {
                    if (hasSameNodes(onShowRationaleAnnotation.getAttributeValues(), needsPermissionAnnotation.getAttributeValues())) {
                        found = true;
                    }
                }
                if (!found) {
                    context.report(ISSUE, context.getLocation(onShowRationaleAnnotation), "Useless @OnShowRationale declaration");
                }
            }
        }

        private static boolean hasSameNodes(List<UNamedExpression> first, List<UNamedExpression> second) {
            if (first.size() != second.size()) {
                return false;
            }
            for (int i = 0, size = first.size(); i < size; i++) {
                if (!first.get(i).toString().equals(second.get(i).toString())) {
                    return false;
                }
            }
            return true;
        }
    }
}


================================================
FILE: lint/src/main/java/permissions/dispatcher/NoDelegateOnResumeDetector.java
================================================
package permissions.dispatcher;

import com.android.tools.lint.client.api.UElementHandler;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.intellij.psi.PsiType;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UExpression;
import org.jetbrains.uast.UIdentifier;
import org.jetbrains.uast.UMethod;
import org.jetbrains.uast.UastVisibility;
import org.jetbrains.uast.visitor.AbstractUastVisitor;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;

public class NoDelegateOnResumeDetector extends Detector implements Detector.UastScanner {
    static final Issue ISSUE = Issue.create("NoDelegateOnResumeDetector",
            "Asking permissions at `onResume()` will cause an infinite loop.",
            "Asking permissions in `onResume()` will cause an infinite loop when user selects \"Never ask again\" and denies the permission, since `onResume()` is called again after returning from a permission request check. This problem can be solved by moving this method call to `onStart()`.",
            Category.CORRECTNESS,
            4,
            Severity.ERROR,
            new Implementation(NoDelegateOnResumeDetector.class,
                    EnumSet.of(Scope.JAVA_FILE)));

    @Override
    public List<Class<? extends UElement>> getApplicableUastTypes() {
        return Collections.<Class<? extends UElement>>singletonList(UClass.class);
    }

    @Override
    public UElementHandler createUastHandler(final JavaContext context) {
        return new UElementHandler() {
            @Override
            public void visitClass(UClass node) {
                if (node.findAnnotation("permissions.dispatcher.RuntimePermissions") != null) {
                    node.accept(new Checker(context));
                }
            }
        };
    }

    private class Checker extends AbstractUastVisitor {
        private final JavaContext context;
        private String needPermissionMethodName = null;

        private Checker(JavaContext context) {
            this.context = context;
        }

        @Override
        public boolean visitMethod(UMethod node) {
            super.visitMethod(node);
            if (node.findAnnotation("permissions.dispatcher.NeedsPermission") != null) {
                needPermissionMethodName = node.getName();
            }
            return true;
        }

        @Override
        public void afterVisitClass(UClass node) {
            super.afterVisitClass(node);
            if (needPermissionMethodName != null) {
                node.accept(new OnResumeChecker(context, node, needPermissionMethodName));
            }
        }
    }

    private class OnResumeChecker extends AbstractUastVisitor {
        private final JavaContext context;
        private boolean isKotlin;
        private UClass uClass;
        private String needPermissionMethodName;

        private OnResumeChecker(JavaContext context, UClass uClass, String needPermissionMethodName) {
            this.context = context;
            this.uClass = uClass;
            this.needPermissionMethodName = needPermissionMethodName;
            isKotlin = context.getPsiFile() != null && "kotlin".equals(context.getPsiFile().getLanguage().getID());
        }

        /**
         * @return return true if visiting end (if lint does not visit inside the method), false otherwise
         */
        @Override
        public boolean visitMethod(UMethod node) {
            super.visitMethod(node);
            return !"onResume".equalsIgnoreCase(node.getName())
                    || node.getReturnType() != PsiType.VOID
                    || node.getUastParameters().size() != 0
                    || !isPublicOrProtected(node);
        }

        private boolean isPublicOrProtected(UMethod node) {
            UastVisibility visibility = node.getVisibility();
            return visibility == UastVisibility.PUBLIC || visibility == UastVisibility.PROTECTED;
        }

        @Override
        public boolean visitCallExpression(UCallExpression node) {
            super.visitCallExpression(node);
            UIdentifier methodIdentifier = node.getMethodIdentifier();
            if (methodIdentifier != null && isWithPermissionCheckCalled(node, methodIdentifier)) {
                context.report(ISSUE, context.getLocation(node), "Asking permission inside onResume()");
            }
            return true;
        }

        private boolean isWithPermissionCheckCalled(@NotNull UCallExpression node, @NotNull UIdentifier methodIdentifier) {
            if (!(needPermissionMethodName + "WithPermissionCheck").equalsIgnoreCase(methodIdentifier.getName())) {
                return false;
            }

            UExpression receiver = node.getReceiver();
            if (isKotlin) {
                return receiver == null;
            } else {
                String qualifiedName = uClass.getQualifiedName();
                if (node.getValueArgumentCount() < 1) return false;
                PsiType expressionType = node.getValueArguments().get(0).getExpressionType();
                return receiver != null
                        && qualifiedName != null
                        && expressionType != null
                        && qualifiedName.equalsIgnoreCase(expressionType.getCanonicalText())
                        && (uClass.getName() + "PermissionsDispatcher").equalsIgnoreCase(receiver.asSourceString());
            }
        }
    }
}


================================================
FILE: lint/src/main/java/permissions/dispatcher/PermissionsDispatcherIssueRegistry.java
================================================
package permissions.dispatcher;

import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Issue;

import java.util.Arrays;
import java.util.List;

@SuppressWarnings("unused")
public final class PermissionsDispatcherIssueRegistry extends IssueRegistry {
    @Override
    public List<Issue> getIssues() {
        return Arrays.asList(
                CallNeedsPermissionDetector.ISSUE,
                CallOnRequestPermissionsResultDetector.ISSUE,
                NoCorrespondingNeedsPermissionDetector.ISSUE,
                NoDelegateOnResumeDetector.ISSUE);
    }

    @Override
    public int getApi() {
        return com.android.tools.lint.detector.api.ApiKt.CURRENT_API;
    }
}


================================================
FILE: lint/src/test/java/permissions/dispatcher/CallNeedsPermissionDetectorKtTest.kt
================================================
package permissions.dispatcher

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestFiles.kt
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import org.intellij.lang.annotations.Language
import org.junit.Test
import permissions.dispatcher.Utils.onNeedsPermission
import permissions.dispatcher.Utils.runtimePermission

class CallNeedsPermissionDetectorKtTest {

    @Test
    fun callNeedsPermissionMethod() {
        @Language("kotlin") val foo = """
                package com.example

                import permissions.dispatcher.NeedsPermission
                import permissions.dispatcher.RuntimePermissions

                @RuntimePermissions
                class Foo {
                    @NeedsPermission("Test")
                    fun fooBar() {
                    }

                    fun hoge() {
                        fooBar()
                    }
                }
                """.trimMargin()

        val expectedText = """
            |src/com/example/Foo.kt:13: Error: Trying to access permission-protected method directly [CallNeedsPermission]
            |                        fooBar()
            |                        ~~~~~~~~
            |1 errors, 0 warnings
            """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        kt(foo))
                .issues(CallNeedsPermissionDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }

    @Test
    fun callNeedsPermissionMethodNoError() {
        @Language("kotlin") val foo = """
                package com.example

                class Foo {
                    fun someMethod() {
                        val baz = Baz()
                        baz.noFooBar()
                    }
                }
                """.trimMargin()

        @Language("kotlin") val baz = """
                package com.example

                import permissions.dispatcher.NeedsPermission
                import permissions.dispatcher.RuntimePermissions

                @RuntimePermissions
                class Baz {
                    @NeedsPermission("Test")
                    fun fooBar() {
                    }

                    fun noFooBar() {
                    }
                }
                """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        kt(foo),
                        kt(baz))
                .issues(CallNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    fun issues502() {
        @Language("kotlin") val foo = """
            package com.example

            import permissions.dispatcher.NeedsPermission
            import permissions.dispatcher.RuntimePermissions

            @RuntimePermissions
            class Foo: AppCompatActivity  {
                @NeedsPermission(Manifest.permission.READ_SMS)
                fun requestOTP() {
                    PhoneVerificationInputFragment().requestOTP()
                }
            }

            class FooFragment: Fragment {
                fun resendOTP() {
                    requestOTP()
                }
                private fun requestOTP() {
                }
            }
        """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        kt(foo))
                .issues(CallNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    fun `same name methods in different class(issue602)`() {
        @Language("kotlin") val foo = """
            package com.example

            import permissions.dispatcher.NeedsPermission
            import permissions.dispatcher.RuntimePermissions

            @RuntimePermissions
            class FirstActivity : AppCompatActivity() {
                @NeedsPermission(Manifest.permission.CAMERA)
                fun someFun() {
                }
            }

            @RuntimePermissions
            class SecondActivity : AppCompatActivity() {
                override fun onCreate(savedInstanceState: Bundle?) {
                    super.onCreate(savedInstanceState)
                    someFun()
                }

                fun someFun() {
                }

                @NeedsPermission(Manifest.permission.CAMERA)
                fun otherFun() {
                }
            }
        """.trimMargin()

        lint()
                .files(java(runtimePermission), java(onNeedsPermission), kt(foo))
                .issues(CallNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }
}


================================================
FILE: lint/src/test/java/permissions/dispatcher/CallNeedsPermissionDetectorTest.kt
================================================
package permissions.dispatcher

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import org.intellij.lang.annotations.Language
import org.junit.Test
import permissions.dispatcher.Utils.onNeedsPermission
import permissions.dispatcher.Utils.runtimePermission

class CallNeedsPermissionDetectorTest {

    @Test
    @Throws(Exception::class)
    fun callNeedsPermissionMethod() {
        @Language("JAVA") val foo = """
                package com.example;

                import permissions.dispatcher.NeedsPermission;
                import permissions.dispatcher.RuntimePermissions;

                @RuntimePermissions
                public class Foo {
                    @NeedsPermission("Test")
                    void fooBar() {
                    }

                    public void hoge() {
                        fooBar();
                    }
                }
                """.trimMargin()

        val expectedText = """
            |src/com/example/Foo.java:13: Error: Trying to access permission-protected method directly [CallNeedsPermission]
            |                        fooBar();
            |                        ~~~~~~~~
            |1 errors, 0 warnings
            """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        java(foo))
                .issues(CallNeedsPermissionDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }

    @Test
    @Throws(Exception::class)
    fun callNeedsPermissionMethodNoError() {
        @Language("JAVA") val foo = """
                package com.example;

                public class Foo {
                    void someMethod() {
                        Baz baz = new Baz();
                        baz.noFooBar();
                    }
                }
                """.trimMargin()

        @Language("JAVA") val baz = """
                package com.example;

                import permissions.dispatcher.NeedsPermission;
                import permissions.dispatcher.RuntimePermissions;

                @RuntimePermissions
                public class Baz {
                    @NeedsPermission("Test")
                    void fooBar() {
                    }

                    void noFooBar() {
                    }
                }
                """.trimMargin()

        lint()
                .files(
                        java(onNeedsPermission),
                        java(foo),
                        java(baz))
                .issues(CallNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun issues502() {
        @Language("JAVA") val foo = """
            package com.example;

            import permissions.dispatcher.NeedsPermission;
            import permissions.dispatcher.RuntimePermissions;

            @RuntimePermissions
            public class Foo extends AppCompatActivity  {
                @NeedsPermission({Manifest.permission.READ_SMS})
                void requestOTP() {
                    new PhoneVerificationInputFragment().requestOTP();
                }
            }

            class FooFragment extends Fragment {
                public void resendOTP() {
                    requestOTP();
                }
                private void requestOTP() {
                }
            }
        """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        java(foo))
                .issues(CallNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    fun `same name methods in different class(issue602)`() {
        @Language("java") val foo = """
            package com.example;

            import permissions.dispatcher.NeedsPermission;
            import permissions.dispatcher.RuntimePermissions;

            @RuntimePermissions
            public class FirstActivity extends AppCompatActivity  {
                @NeedsPermission({Manifest.permission.READ_SMS})
                void someFun() {
                }
            }

            @RuntimePermissions
            public class SecondActivity extends AppCompatActivity  {
                @Override
                protected void onCreate(@Nullable Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    someFun();
                }

                void someFun() {
                }

                @NeedsPermission({Manifest.permission.READ_SMS})
                void otherFun() {
                }
            }
        """.trimMargin()

        lint()
                .files(java(runtimePermission), java(onNeedsPermission), java(foo))
                .issues(CallNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }
}


================================================
FILE: lint/src/test/java/permissions/dispatcher/CallOnRequestPermissionsResultDetectorKtTest.kt
================================================
package permissions.dispatcher

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestFiles.kt
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import org.intellij.lang.annotations.Language
import org.junit.Test
import permissions.dispatcher.Utils.onNeedsPermission
import permissions.dispatcher.Utils.onRationaleAnnotation
import permissions.dispatcher.Utils.runtimePermission

class CallOnRequestPermissionsResultDetectorKtTest {
    @Test
    @Throws(Exception::class)
    fun callOnRequestPermissionsResultDetectorNoErrorForKotlin() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
                        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
                        onRequestPermissionsResult(requestCode, grantResults)
                    }

                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    @OnShowRationale("Camera")
                    fun someMethod() {
                    }
                }
                """.trimMargin()

        @Language("kotlin") val generatedClass = """
                package permissions.dispatcher

                fun Foo.onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) {
                }
                """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        java(onRationaleAnnotation),
                        kt(generatedClass),
                        kt(foo))
                .issues(CallOnRequestPermissionsResultDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun callOnRequestPermissionsResultDetectorNoErrorWithNamedMethodReference() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) =
                        onRequestPermissionsResult(requestCode, grantResults)

                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    @OnShowRationale("Camera")
                    fun someMethod() {
                    }
                }
                """.trimMargin()

        @Language("kotlin") val generatedClass = """
                package permissions.dispatcher

                fun Foo.onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) {
                }
                """.trimMargin()

        lint()
            .files(
                java(runtimePermission),
                java(onNeedsPermission),
                java(onRationaleAnnotation),
                kt(generatedClass),
                kt(foo))
            .issues(CallOnRequestPermissionsResultDetector.ISSUE)
            .run()
            .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun callOnRequestPermissionsResultDetectorErrorWithWrongMethod() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) =
                        hoge(requestCode, grantResults)

                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    @OnShowRationale("Camera")
                    fun someMethod() {
                    }
                }
                """.trimMargin()

        @Language("kotlin") val generatedClass = """
                package permissions.dispatcher

                fun Foo.hoge(requestCode: Int, grantResults: IntArray) {
                }
                """.trimMargin()

        val expectedText = """
                |src/permissions/dispatcher/Foo.kt:5: Error: Generated onRequestPermissionsResult method not called [NeedOnRequestPermissionsResult]
                |                    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) =
                |                    ^
                |1 errors, 0 warnings
                """.trimMargin()

        lint()
            .files(
                java(runtimePermission),
                java(onNeedsPermission),
                java(onRationaleAnnotation),
                kt(foo))
            .issues(CallOnRequestPermissionsResultDetector.ISSUE)
            .run()
            .expect(expectedText)
            .expectErrorCount(1)
            .expectWarningCount(0)
    }

    @Test
    @Throws(Exception::class)
    fun callOnRequestPermissionsResultDetector() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo: android.app.Activity {
                    fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
                        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
                    }

                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    @OnShowRationale("Camera")
                    fun someMethod() {
                    }
                }
                """.trimMargin()

        val expectedText = """
                |src/permissions/dispatcher/Foo.kt:5: Error: Generated onRequestPermissionsResult method not called [NeedOnRequestPermissionsResult]
                |                    fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
                |                    ^
                |1 errors, 0 warnings
                """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        java(onRationaleAnnotation),
                        kt(foo))
                .issues(CallOnRequestPermissionsResultDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }
}


================================================
FILE: lint/src/test/java/permissions/dispatcher/CallOnRequestPermissionsResultDetectorTest.kt
================================================
package permissions.dispatcher

import org.intellij.lang.annotations.Language
import org.junit.Test

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import permissions.dispatcher.Utils.onNeedsPermission
import permissions.dispatcher.Utils.onRationaleAnnotation
import permissions.dispatcher.Utils.runtimePermission

class CallOnRequestPermissionsResultDetectorTest {

    @Test
    @Throws(Exception::class)
    fun callOnRequestPermissionsResultDetectorNoError() {
        @Language("JAVA") val foo = """
                package permissions.dispatcher;

                @RuntimePermissions
                public class Foo extends android.app.Activity {
                    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
                        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
                        FooPermissionsDispatcher.onRequestPermissionsResult(requestCode, grantResults);
                    }

                    @NeedsPermission("Camera")
                    public void showCamera() {
                    }

                    @OnShowRationale("Camera")
                    public void someMethod() {
                    }
                }
                """.trimMargin()

        @Language("JAVA") val generatedClass = """
                package permissions.dispatcher;

                public class FooPermissionsDispatcher {
                    public static void onRequestPermissionsResult(int requestCode, int[] grantResults) {
                    }
                }
                """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        java(onRationaleAnnotation),
                        java(generatedClass),
                        java(foo))
                .issues(CallOnRequestPermissionsResultDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun callOnRequestPermissionsResultDetector() {
        @Language("JAVA") val foo = """
                package permissions.dispatcher;

                @RuntimePermissions
                public class Foo extends android.app.Activity {
                    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
                        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
                    }

                    @NeedsPermission("Camera")
                    public void showCamera() {
                    }

                    @OnShowRationale("Camera")
                    public void someMethod() {
                    }
                }
                """.trimMargin()

        val expectedText = """
                |src/permissions/dispatcher/Foo.java:5: Error: Generated onRequestPermissionsResult method not called [NeedOnRequestPermissionsResult]
                |                    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
                |                    ^
                |1 errors, 0 warnings
                """.trimMargin()

        lint()
                .files(
                        java(runtimePermission),
                        java(onNeedsPermission),
                        java(onRationaleAnnotation),
                        java(foo))
                .issues(CallOnRequestPermissionsResultDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }
}


================================================
FILE: lint/src/test/java/permissions/dispatcher/NoCorrespondingNeedsPermissionDetectorKtTest.kt
================================================
package permissions.dispatcher

import org.intellij.lang.annotations.Language
import org.junit.Test

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestFiles.kt
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import permissions.dispatcher.Utils.onNeedsPermission
import permissions.dispatcher.Utils.onRationaleAnnotation

class NoCorrespondingNeedsPermissionDetectorKtTest {

    @Test
    @Throws(Exception::class)
    fun noNeedsPermissionAnnotationNoErrors() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                class Foo {
                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    @OnShowRationale("Camera")
                    fun someMethod() {
                    }
                }
                """.trimMargin()

        lint()
                .files(
                        java(onNeedsPermission),
                        java(onRationaleAnnotation),
                        kt(foo))
                .issues(NoCorrespondingNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun noNeedsPermissionAnnotationNoErrorsOrderNotMatter() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                class Foo {
                    @OnShowRationale("Camera")
                    fun someMethod() {
                    }

                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }
                }
                """.trimMargin()

        lint()
                .files(
                        java(onNeedsPermission),
                        java(onRationaleAnnotation),
                        kt(foo))
                .issues(NoCorrespondingNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun noNeedsPermissionAnnotation() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                class Foo {
                    @OnShowRationale("Camera")
                    fun someMethod() {
                    }
                }
                """.trimMargin()

        val expectedText = """
                |src/permissions/dispatcher/Foo.kt:4: Error: Useless @OnShowRationale declaration [NoCorrespondingNeedsPermission]
                |                    @OnShowRationale("Camera")
                |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~
                |1 errors, 0 warnings
                """.trimMargin()

        lint()
                .files(
                        java(onRationaleAnnotation),
                        kt(foo))
                .issues(NoCorrespondingNeedsPermissionDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }
}


================================================
FILE: lint/src/test/java/permissions/dispatcher/NoCorrespondingNeedsPermissionDetectorTest.kt
================================================
package permissions.dispatcher

import org.intellij.lang.annotations.Language
import org.junit.Test

import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
import permissions.dispatcher.Utils.onNeedsPermission
import permissions.dispatcher.Utils.onRationaleAnnotation

class NoCorrespondingNeedsPermissionDetectorTest {

    @Test
    @Throws(Exception::class)
    fun noNeedsPermissionAnnotationNoErrors() {
        @Language("JAVA") val foo = """
                package permissions.dispatcher;

                public class Foo {
                    @NeedsPermission("Camera")
                    void showCamera() {
                    }

                    @OnShowRationale("Camera")
                    void someMethod() {
                    }
                }
                """.trimMargin()

        lint()
                .files(
                        java(onNeedsPermission),
                        java(onRationaleAnnotation),
                        java(foo))
                .issues(NoCorrespondingNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun noNeedsPermissionAnnotationNoErrorsOrderNoTMatter() {
        @Language("JAVA") val foo = """
                package permissions.dispatcher;

                public class Foo {
                    @OnShowRationale("Camera")
                    void someMethod() {
                    }

                    @NeedsPermission("Camera")
                    void showCamera() {
                    }
                }
                """.trimMargin()

        lint()
                .files(
                        java(onNeedsPermission),
                        java(onRationaleAnnotation),
                        java(foo))
                .issues(NoCorrespondingNeedsPermissionDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun noNeedsPermissionAnnotation() {
        @Language("JAVA") val foo = """
                package permissions.dispatcher;

                public class Foo {
                    @OnShowRationale("Camera")
                    void someMethod() {
                    }
                }
                """.trimMargin()

        val expectedText = """
                |src/permissions/dispatcher/Foo.java:4: Error: Useless @OnShowRationale declaration [NoCorrespondingNeedsPermission]
                |                    @OnShowRationale("Camera")
                |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~
                |1 errors, 0 warnings
                """.trimMargin()

        lint()
                .files(
                        java(onRationaleAnnotation),
                        java(foo))
                .issues(NoCorrespondingNeedsPermissionDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }
}


================================================
FILE: lint/src/test/java/permissions/dispatcher/NoDelegateOnResumeDetectorKtTest.kt
================================================
package permissions.dispatcher

import com.android.tools.lint.checks.infrastructure.TestFiles
import com.android.tools.lint.checks.infrastructure.TestLintTask
import org.intellij.lang.annotations.Language
import org.junit.Test

import permissions.dispatcher.Utils.runtimePermission

class NoDelegateOnResumeDetectorKtTest {
    @Test
    @Throws(Exception::class)
    fun `WithPermissionCheck call inside onResume`() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    fun onResume() {
                        super.onResume()
                        showCameraWithPermissionCheck()
                    }
                }
                """.trimMargin()

        val expectedText = """
                |src/permissions/dispatcher/Foo.kt:11: Error: Asking permission inside onResume() [NoDelegateOnResumeDetector]
                |                        showCameraWithPermissionCheck()
                |                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                |1 errors, 0 warnings
                """.trimMargin()

        TestLintTask.lint()
                .files(
                        TestFiles.java(runtimePermission),
                        TestFiles.java(Utils.onNeedsPermission),
                        TestFiles.kt(foo))
                .issues(NoDelegateOnResumeDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }

    @Test
    @Throws(Exception::class)
    fun `WithPermissionCheck call inside onResume which the method is before NeedsPermission`() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    fun onResume() {
                        super.onResume()
                        showCameraWithPermissionCheck()
                    }

                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }
                }
                """.trimMargin()

        val expectedText = """
                |src/permissions/dispatcher/Foo.kt:7: Error: Asking permission inside onResume() [NoDelegateOnResumeDetector]
                |                        showCameraWithPermissionCheck()
                |                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                |1 errors, 0 warnings
                """.trimMargin()

        TestLintTask.lint()
                .files(
                        TestFiles.java(runtimePermission),
                        TestFiles.java(Utils.onNeedsPermission),
                        TestFiles.kt(foo))
                .issues(NoDelegateOnResumeDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }

    @Test
    @Throws(Exception::class)
    fun `No WithPermissionCheck inside onResume`() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    fun onStart() {
                        super.onStart()
                        showCameraWithPermissionCheck()
                    }
                }
                """.trimMargin()

        TestLintTask.lint()
                .files(
                        TestFiles.java(runtimePermission),
                        TestFiles.java(Utils.onNeedsPermission),
                        TestFiles.kt(foo))
                .issues(NoDelegateOnResumeDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun `WithPermissionCheck call inside onResume with argument`() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    @NeedsPermission("Camera")
                    fun showCamera(value: Int) {
                    }

                    fun onResume() {
                        super.onResume()
                        showCameraWithPermissionCheck(1)
                    }

                }
                """.trimMargin()

        val expectedText = """
                |src/permissions/dispatcher/Foo.kt:11: Error: Asking permission inside onResume() [NoDelegateOnResumeDetector]
                |                        showCameraWithPermissionCheck(1)
                |                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                |1 errors, 0 warnings
                """.trimMargin()

        TestLintTask.lint()
                .files(
                        TestFiles.java(runtimePermission),
                        TestFiles.java(Utils.onNeedsPermission),
                        TestFiles.kt(foo))
                .issues(NoDelegateOnResumeDetector.ISSUE)
                .run()
                .expect(expectedText)
                .expectErrorCount(1)
                .expectWarningCount(0)
    }

    @Test
    @Throws(Exception::class)
    fun `WithPermissionCheck call inside onResume but there is receiver`() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    fun onResume() {
                        super.onResume()
                        Bar.showCameraWithPermissionCheck()
                    }
                }
                """.trimMargin()

        TestLintTask.lint()
                .files(
                        TestFiles.java(runtimePermission),
                        TestFiles.java(Utils.onNeedsPermission),
                        TestFiles.kt(foo))
                .issues(NoDelegateOnResumeDetector.ISSUE)
                .run()
                .expectClean()
    }

    @Test
    @Throws(Exception::class)
    fun `WithPermissionCheck call inside onResume but its visibility is private`() {
        @Language("kotlin") val foo = """
                package permissions.dispatcher

                @RuntimePermissions
                class Foo : android.app.Activity {
                    @NeedsPermission("Camera")
                    fun showCamera() {
                    }

                    private fun onResume() {
                        super.onResume()
                        showCameraWithPermissionCheck()
                    }
                }
                """.trimMargin()

        TestLintTask.lint()
                .files(
                        TestFiles.java(runtimePermission),
                        TestFiles.java(Utils.onNeedsPermission),
                        TestFiles.kt(foo))
                .issues(NoDelegateOnResumeDetector.ISSUE)
                .run()
                .expectClean()
    }

}

================================================
FILE: lint/src/test/java/permissions/dispatcher/NoDelegateOnResumeDetectorTest.kt
================================================
package permissions.dispatcher

import com.android.tools.lint.checks.infrastructure.TestFiles
import com.android.tools.lint.checks.infrastructure.TestLintTask
import org.intellij.lang.annotations.Language
import org.junit.Test

import permissions.dispatcher.Utils.runtimePermission

class NoDelegateOnResumeDetectorTest {
    @Test
    @Throws(Exception::class)
    fun `WithPermissionCheck call inside onResume`() {
        @Language("JAVA") val foo = """
                package permissions.dispatcher;

                @RuntimePermissions
                public class Foo {
                    @NeedsPermission("Camera")
                    void showCamera() {
                    }

                    protected void onResume() {
                        super.onResume();
                        FooPermissionsDispatcher
Download .txt
gitextract_y6ni10jf/

├── .github/
│   ├── ISSUE_TEMPLATE.md
│   └── workflows/
│       ├── deploy_snapshot.yml
│       ├── ktx-release.yml
│       ├── pull_request_ci.yml
│       └── release.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── annotation/
│   ├── .gitignore
│   ├── build.gradle
│   ├── gradle.properties
│   └── src/
│       └── main/
│           └── java/
│               └── permissions/
│                   └── dispatcher/
│                       ├── GrantableRequest.java
│                       ├── NeedsPermission.java
│                       ├── OnNeverAskAgain.java
│                       ├── OnPermissionDenied.java
│                       ├── OnShowRationale.java
│                       ├── PermissionRequest.java
│                       └── RuntimePermissions.java
├── build.gradle
├── buildSrc/
│   ├── build.gradle
│   └── src/
│       └── main/
│           └── groovy/
│               └── permissions/
│                   └── dispatcher/
│                       ├── AarToJarDependencyPlugin.groovy
│                       ├── AndroidJarDependencyExtension.groovy
│                       └── AndroidJarDependencyPlugin.groovy
├── doc/
│   ├── java_usage.md
│   ├── maxsdkversion.md
│   ├── migration_guide.md
│   └── special_permissions.md
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── ktx/
│   ├── README.md
│   ├── build.gradle
│   ├── gradle.properties
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   └── java/
│       │       └── permissions/
│       │           └── dispatcher/
│       │               └── ktx/
│       │                   ├── ActivityExtensions.kt
│       │                   ├── Event.kt
│       │                   ├── FragmentExtensions.kt
│       │                   ├── KtxPermissionRequest.kt
│       │                   ├── LocationPermission.kt
│       │                   ├── PermissionRequestFragment.kt
│       │                   ├── PermissionRequestResult.kt
│       │                   ├── PermissionRequestType.kt
│       │                   ├── PermissionRequestViewModel.kt
│       │                   ├── PermissionsRequester.kt
│       │                   ├── PermissionsRequesterImpl.kt
│       │                   └── TypeAliases.kt
│       └── test/
│           ├── java/
│           │   └── permissions/
│           │       └── dispatcher/
│           │           └── test/
│           │               ├── EventTest.kt
│           │               ├── KtxPermissionRequestTest.kt
│           │               ├── LocationPermissionTest.kt
│           │               └── PermissionRequestViewModelTest.kt
│           └── resources/
│               └── mockito-extensions/
│                   └── org.mockito.plugins.MockMaker
├── ktx-sample/
│   ├── .gitignore
│   ├── build.gradle
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── permissions/
│           │       └── dispatcher/
│           │           └── ktx/
│           │               └── sample/
│           │                   ├── MainActivity.kt
│           │                   └── MainFragment.kt
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── fragment_camera.xml
│               │   └── fragment_main.xml
│               └── values/
│                   ├── dimens.xml
│                   ├── strings.xml
│                   └── template-styles.xml
├── library/
│   ├── build.gradle
│   ├── gradle.properties
│   └── src/
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   └── java/
│       │       └── permissions/
│       │           └── dispatcher/
│       │               └── PermissionUtils.java
│       └── test/
│           └── java/
│               └── permissions/
│                   └── dispatcher/
│                       └── ApiLevelTestSuite.java
├── lint/
│   ├── .gitignore
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── permissions/
│       │           └── dispatcher/
│       │               ├── CallNeedsPermissionDetector.java
│       │               ├── CallOnRequestPermissionsResultDetector.java
│       │               ├── NoCorrespondingNeedsPermissionDetector.java
│       │               ├── NoDelegateOnResumeDetector.java
│       │               └── PermissionsDispatcherIssueRegistry.java
│       └── test/
│           └── java/
│               └── permissions/
│                   └── dispatcher/
│                       ├── CallNeedsPermissionDetectorKtTest.kt
│                       ├── CallNeedsPermissionDetectorTest.kt
│                       ├── CallOnRequestPermissionsResultDetectorKtTest.kt
│                       ├── CallOnRequestPermissionsResultDetectorTest.kt
│                       ├── NoCorrespondingNeedsPermissionDetectorKtTest.kt
│                       ├── NoCorrespondingNeedsPermissionDetectorTest.kt
│                       ├── NoDelegateOnResumeDetectorKtTest.kt
│                       ├── NoDelegateOnResumeDetectorTest.kt
│                       ├── PermissionsDispatcherIssueRegistryTest.kt
│                       └── Utils.kt
├── processor/
│   ├── build.gradle
│   ├── gradle.properties
│   └── src/
│       ├── main/
│       │   ├── kotlin/
│       │   │   └── permissions/
│       │   │       └── dispatcher/
│       │   │           └── processor/
│       │   │               ├── PermissionsProcessor.kt
│       │   │               ├── ProcessorUnit.kt
│       │   │               ├── RequestCodeProvider.kt
│       │   │               ├── RuntimePermissionsElement.kt
│       │   │               ├── exception/
│       │   │               │   ├── DuplicatedMethodNameException.kt
│       │   │               │   ├── DuplicatedValueException.kt
│       │   │               │   ├── MixPermissionTypeException.kt
│       │   │               │   ├── NoAnnotatedMethodsException.kt
│       │   │               │   ├── NoParametersAllowedException.kt
│       │   │               │   ├── NoThrowsAllowedException.kt
│       │   │               │   ├── PrivateMethodException.kt
│       │   │               │   ├── SpecialPermissionsWithNeverAskAgainException.kt
│       │   │               │   ├── WrongClassException.kt
│       │   │               │   ├── WrongParametersException.kt
│       │   │               │   └── WrongReturnTypeException.kt
│       │   │               ├── impl/
│       │   │               │   ├── java/
│       │   │               │   │   ├── JavaActivityProcessorUnit.kt
│       │   │               │   │   ├── JavaBaseProcessorUnit.kt
│       │   │               │   │   ├── JavaFragmentProcessorUnit.kt
│       │   │               │   │   ├── SensitivePermissionInterface.kt
│       │   │               │   │   ├── SystemAlertWindowHelper.kt
│       │   │               │   │   └── WriteSettingsHelper.kt
│       │   │               │   └── kotlin/
│       │   │               │       ├── KotlinActivityProcessorUnit.kt
│       │   │               │       ├── KotlinBaseProcessorUnit.kt
│       │   │               │       ├── KotlinFragmentProcessorUnit.kt
│       │   │               │       ├── SensitivePermissionInterface.kt
│       │   │               │       ├── SystemAlertWindowHelper.kt
│       │   │               │       └── WriteSettingsHelper.kt
│       │   │               └── util/
│       │   │                   ├── Constants.kt
│       │   │                   ├── Extensions.kt
│       │   │                   ├── Helpers.kt
│       │   │                   └── Validators.kt
│       │   └── resources/
│       │       └── META-INF/
│       │           ├── gradle/
│       │           │   └── incremental.annotation.processors
│       │           └── services/
│       │               └── javax.annotation.processing.Processor
│       └── test/
│           ├── java/
│           │   └── permissions/
│           │       └── dispatcher/
│           │           └── processor/
│           │               ├── KtProcessorTestSuite.kt
│           │               ├── ProcessorTestSuite.java
│           │               ├── base/
│           │               │   ├── AndroidAwareClassLoader.java
│           │               │   ├── BaseTest.java
│           │               │   ├── StringEquals.java
│           │               │   └── TestSuite.java
│           │               ├── data/
│           │               │   └── Source.java
│           │               └── util/
│           │                   └── ExtensionsTest.kt
│           └── resources/
│               └── mockito-extensions/
│                   └── org.mockito.plugins.MockMaker
├── sample/
│   ├── .gitignore
│   ├── build.gradle
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── kotlin/
│           │   └── permissions/
│           │       └── dispatcher/
│           │           └── sample/
│           │               ├── MainActivity.kt
│           │               ├── camera/
│           │               │   ├── CameraPreview.kt
│           │               │   └── CameraPreviewFragment.kt
│           │               └── contacts/
│           │                   └── ContactsFragment.kt
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── fragment_camera.xml
│               │   └── fragment_contacts.xml
│               └── values/
│                   ├── dimens.xml
│                   ├── strings.xml
│                   └── template-styles.xml
├── settings.gradle
└── test/
    ├── .gitignore
    ├── build.gradle
    └── src/
        ├── main/
        │   ├── AndroidManifest.xml
        │   └── java/
        │       └── permissions/
        │           └── dispatcher/
        │               └── test/
        │                   ├── ActivityOnlyNeedsPermission.java
        │                   ├── ActivityWithAllAnnotations.java
        │                   ├── ActivityWithAllAnnotationsKt.kt
        │                   ├── ActivityWithNoParameterArgumentKt.kt
        │                   ├── ActivityWithOnNeverAskAgain.java
        │                   ├── ActivityWithOnPermissionDenied.java
        │                   ├── ActivityWithParametersKt.kt
        │                   ├── ActivityWithShowRationale.java
        │                   ├── ActivityWithSystemAlertWindow.java
        │                   ├── ActivityWithSystemAlertWindowAllAnnotations.java
        │                   ├── ActivityWithSystemAlertWindowKt.kt
        │                   ├── ActivityWithSystemAlertWindowKtAllAnnotations.kt
        │                   ├── ActivityWithWriteSetting.java
        │                   ├── ActivityWithWriteSettingAllAnnotations.java
        │                   ├── ActivityWithWriteSettingKt.kt
        │                   ├── ActivityWithWriteSettingKtAllAnnotations.kt
        │                   ├── FragmentWithAllAnnotations.java
        │                   └── FragmentWithAllAnnotationsKt.kt
        └── test/
            └── java/
                └── permissions/
                    └── dispatcher/
                        └── test/
                            ├── ActivityOnlyNeedsPermissionPermissionsDispatcherTest.kt
                            ├── ActivityWithAllAnnotationsKtPermissionsDispatcherTest.kt
                            ├── ActivityWithAllAnnotationsPermissionsDispatcherTest.kt
                            ├── ActivityWithNoParameterArgumentKtPermissionsDispatcherTest.kt
                            ├── ActivityWithOnNeverAskAgainPermissionsDispatcherTest.kt
                            ├── ActivityWithOnPermissionDeniedPermissionsDispatcherTest.kt
                            ├── ActivityWithParametersKtPermissionsDispatcherTest.kt
                            ├── ActivityWithShowRationalePermissionsDispatcherTest.kt
                            ├── ActivityWithSystemAlertWindowAllAnnotationsPermissionsDispatcherTest.kt
                            ├── ActivityWithSystemAlertWindowKtAllAnnotationsTest.kt
                            ├── ActivityWithSystemAlertWindowKtTest.kt
                            ├── ActivityWithSystemAlertWindowPermissionsDispatcherTest.kt
                            ├── ActivityWithWriteSettingAllAnnotationsPermissionsDispatcherTest.kt
                            ├── ActivityWithWriteSettingKtAllAnnotationsTest.kt
                            ├── ActivityWithWriteSettingKtTest.kt
                            ├── ActivityWithWriteSettingPermissionsDispatcherTest.kt
                            ├── Extensions.kt
                            ├── FragmentWithAllAnnotationsKtPermissionsDispatcherTest.kt
                            └── FragmentWithAllAnnotationsPermissionsDispatcherTest.kt
Download .txt
SYMBOL INDEX (480 symbols across 25 files)

FILE: annotation/src/main/java/permissions/dispatcher/GrantableRequest.java
  type GrantableRequest (line 3) | public interface GrantableRequest extends PermissionRequest {
    method grant (line 4) | void grant();

FILE: annotation/src/main/java/permissions/dispatcher/PermissionRequest.java
  type PermissionRequest (line 7) | public interface PermissionRequest {
    method proceed (line 8) | void proceed();
    method cancel (line 10) | void cancel();

FILE: library/src/main/java/permissions/dispatcher/PermissionUtils.java
  class PermissionUtils (line 13) | public final class PermissionUtils {
    method PermissionUtils (line 36) | private PermissionUtils() {
    method verifyPermissions (line 45) | public static boolean verifyPermissions(int... grantResults) {
    method permissionExists (line 63) | private static boolean permissionExists(String permission) {
    method hasSelfPermissions (line 78) | public static boolean hasSelfPermissions(Context context, String... pe...
    method hasSelfPermission (line 98) | private static boolean hasSelfPermission(Context context, String permi...
    method shouldShowRequestPermissionRationale (line 113) | public static boolean shouldShowRequestPermissionRationale(Activity ac...
    method shouldShowRequestPermissionRationale (line 129) | public static boolean shouldShowRequestPermissionRationale(Fragment fr...

FILE: library/src/test/java/permissions/dispatcher/ApiLevelTestSuite.java
  class ApiLevelTestSuite (line 35) | @RunWith(PowerMockRunner.class)
    method ApiLevelTestSuite (line 45) | public ApiLevelTestSuite() {
    method beforeTest (line 50) | @Before
    method testAssumeApiLevelWorking (line 62) | @Test
    method testCheckSelfPermissionMockWorking (line 73) | @Test
    method testAddVoicemailPermission (line 79) | @Test
    method testBodySensorsPermission (line 86) | @Test
    method testReadCallLogPermission (line 93) | @Test
    method testReadExternalStoragePermission (line 100) | @Test
    method testWriteCallLogPermission (line 107) | @Test
    method testBodySensors (line 114) | @Test
    method testReadPhoneNumbers (line 119) | @Test
    method testAnswerPhoneNumbers (line 124) | @Test
    method testAcceptHandOver (line 129) | @Test
    method testActivityRecognition (line 134) | @Test
    method testAccessMediaLocation (line 139) | @Test
    method testAccessBackgroundLocation (line 144) | @Test
    method iteratePermissionCheck (line 151) | private void iteratePermissionCheck(String permission, int permissionM...
    method assumeApiLevel (line 170) | private void assumeApiLevel(int apiLevel) throws Exception {
    method resetApiLevel (line 186) | private void resetApiLevel() throws Exception {

FILE: lint/src/main/java/permissions/dispatcher/CallNeedsPermissionDetector.java
  class CallNeedsPermissionDetector (line 28) | public final class CallNeedsPermissionDetector extends Detector implemen...
    method getApplicableUastTypes (line 42) | @Override
    method createUastHandler (line 47) | @Override
    class AnnotationChecker (line 57) | private static class AnnotationChecker extends AbstractUastVisitor {
      method AnnotationChecker (line 63) | private AnnotationChecker(JavaContext context) {
      method visitAnnotation (line 67) | @Override
      method visitCallExpression (line 76) | @Override
      method methodIdentifier (line 93) | @Nullable
      method visitMethod (line 109) | @Override
      method methodIdentifier (line 132) | @Nullable
      method isGeneratedFiles (line 142) | private static boolean isGeneratedFiles(JavaContext context) {

FILE: lint/src/main/java/permissions/dispatcher/CallOnRequestPermissionsResultDetector.java
  class CallOnRequestPermissionsResultDetector (line 31) | public final class CallOnRequestPermissionsResultDetector extends Detect...
    method getApplicableUastTypes (line 42) | @Override
    method createUastHandler (line 47) | @Override
    class OnRequestPermissionsResultChecker (line 57) | private static class OnRequestPermissionsResultChecker extends Abstrac...
      method OnRequestPermissionsResultChecker (line 68) | private OnRequestPermissionsResultChecker(JavaContext context, UClas...
      method visitAnnotation (line 74) | @Override
      method visitMethod (line 83) | @Override
      method assertMethodName (line 97) | private static boolean assertMethodName(@Nullable String name) {
      method isGeneratedMethodCalled (line 101) | private static boolean isGeneratedMethodCalled(UMethod method, Strin...

FILE: lint/src/main/java/permissions/dispatcher/NoCorrespondingNeedsPermissionDetector.java
  class NoCorrespondingNeedsPermissionDetector (line 24) | public final class NoCorrespondingNeedsPermissionDetector extends Detect...
    method getApplicableUastTypes (line 35) | @Override
    method createUastHandler (line 40) | @Override
    class AnnotationChecker (line 49) | private static class AnnotationChecker extends AbstractUastVisitor {
      method AnnotationChecker (line 57) | private AnnotationChecker(JavaContext context) {
      method visitAnnotation (line 63) | @Override
      method afterVisitClass (line 81) | @Override
      method hasSameNodes (line 97) | private static boolean hasSameNodes(List<UNamedExpression> first, Li...

FILE: lint/src/main/java/permissions/dispatcher/NoDelegateOnResumeDetector.java
  class NoDelegateOnResumeDetector (line 27) | public class NoDelegateOnResumeDetector extends Detector implements Dete...
    method getApplicableUastTypes (line 37) | @Override
    method createUastHandler (line 42) | @Override
    class Checker (line 54) | private class Checker extends AbstractUastVisitor {
      method Checker (line 58) | private Checker(JavaContext context) {
      method visitMethod (line 62) | @Override
      method afterVisitClass (line 71) | @Override
    class OnResumeChecker (line 80) | private class OnResumeChecker extends AbstractUastVisitor {
      method OnResumeChecker (line 86) | private OnResumeChecker(JavaContext context, UClass uClass, String n...
      method visitMethod (line 96) | @Override
      method isPublicOrProtected (line 105) | private boolean isPublicOrProtected(UMethod node) {
      method visitCallExpression (line 110) | @Override
      method isWithPermissionCheckCalled (line 120) | private boolean isWithPermissionCheckCalled(@NotNull UCallExpression...

FILE: lint/src/main/java/permissions/dispatcher/PermissionsDispatcherIssueRegistry.java
  class PermissionsDispatcherIssueRegistry (line 9) | @SuppressWarnings("unused")
    method getIssues (line 11) | @Override
    method getApi (line 20) | @Override

FILE: processor/src/test/java/permissions/dispatcher/processor/ProcessorTestSuite.java
  class ProcessorTestSuite (line 7) | public class ProcessorTestSuite extends TestSuite {
    method noPermissionActivity (line 8) | @Test public void noPermissionActivity() {
    method permissionWithNonVoidReturnType (line 13) | @Test public void permissionWithNonVoidReturnType() {
    method rationaleWithNonVoidReturnType (line 18) | @Test public void rationaleWithNonVoidReturnType() {
    method deniedWithNonVoidReturnType (line 23) | @Test public void deniedWithNonVoidReturnType() {
    method neverAskWithNonVoidReturnType (line 28) | @Test public void neverAskWithNonVoidReturnType() {
    method rationaleWithWrongParameters (line 33) | @Test public void rationaleWithWrongParameters() {
    method rationaleWithOneMoreParameters (line 38) | @Test public void rationaleWithOneMoreParameters() {
    method deniedWithParameters (line 43) | @Test public void deniedWithParameters() {
    method neverAskWithParameters (line 48) | @Test public void neverAskWithParameters() {
    method permissionWithThrows (line 53) | @Test public void permissionWithThrows() {
    method rationaleWithThrows (line 58) | @Test public void rationaleWithThrows() {
    method deniedWithThrows (line 63) | @Test public void deniedWithThrows() {
    method neverAskWithThrows (line 68) | @Test public void neverAskWithThrows() {
    method privatePermission (line 73) | @Test public void privatePermission() {
    method privateRationale (line 78) | @Test public void privateRationale() {
    method privateDenied (line 83) | @Test public void privateDenied() {
    method privateNeverAsk (line 88) | @Test public void privateNeverAsk() {
    method wrongAnnotatedClass (line 93) | @Test public void wrongAnnotatedClass() {
    method duplicatedRationale (line 98) | @Test public void duplicatedRationale() {
    method duplicatedDenied (line 103) | @Test public void duplicatedDenied() {
    method duplicatedNeverAsk (line 108) | @Test public void duplicatedNeverAsk() {
    method needsPermissionMethodOverload (line 113) | @Test public void needsPermissionMethodOverload() {
    method systemAlertWindowWithOnNeverAskAgain (line 118) | @Test public void systemAlertWindowWithOnNeverAskAgain() {
    method writeSettingsWithOnNeverAskAgain (line 123) | @Test public void writeSettingsWithOnNeverAskAgain() {
    method onePermissionActivity (line 128) | @Test public void onePermissionActivity() {
    method onePermissionSupportFragment (line 132) | @Test public void onePermissionSupportFragment() {
    method onePermissionWithParametersActivity (line 136) | @Test public void onePermissionWithParametersActivity() {
    method onePermissionWithParametersAndRationaleActivity (line 140) | @Test public void onePermissionWithParametersAndRationaleActivity() {
    method onePermissionWithParametersAndDeniedActivity (line 144) | @Test public void onePermissionWithParametersAndDeniedActivity() {
    method onePermissionWithParametersRationaleAndDeniedActivity (line 148) | @Test public void onePermissionWithParametersRationaleAndDeniedActivit...
    method onePermissionWithParametersFragment (line 152) | @Test public void onePermissionWithParametersFragment() {
    method onePermissionWithParametersAndRationaleFragment (line 156) | @Test public void onePermissionWithParametersAndRationaleFragment() {
    method onePermissionWithParametersAndDeniedFragment (line 160) | @Test public void onePermissionWithParametersAndDeniedFragment() {
    method onePermissionWithParametersRationaleAndDeniedFragment (line 164) | @Test public void onePermissionWithParametersRationaleAndDeniedFragmen...
    method onePermissionWithNeverAskActivity (line 168) | @Test public void onePermissionWithNeverAskActivity() {
    method onePermissionWithNeverAskAndRationaleActivity (line 172) | @Test public void onePermissionWithNeverAskAndRationaleActivity() {
    method onePermissionWithNeverAskRationaleAndDeniedActivity (line 176) | @Test public void onePermissionWithNeverAskRationaleAndDeniedActivity() {
    method onePermissionWithNeverAskAndDeniedActivity (line 180) | @Test public void onePermissionWithNeverAskAndDeniedActivity() {
    method twoPermissionsWithNeverAskActivity (line 184) | @Test public void twoPermissionsWithNeverAskActivity() {
    method twoPermissionsActivity (line 188) | @Test public void twoPermissionsActivity() {
    method twoPermissionsWithSameSignatureActivity (line 192) | @Test public void twoPermissionsWithSameSignatureActivity() {
    method twoPermissionsWithSameSignatureAndRationaleActivity (line 196) | @Test public void twoPermissionsWithSameSignatureAndRationaleActivity() {
    method twoPermissionsWithSameSignatureAndDeniedActivity (line 200) | @Test public void twoPermissionsWithSameSignatureAndDeniedActivity() {
    method twoPermissionsWithSameSignatureRationaleAndDeniedActivity (line 204) | @Test public void twoPermissionsWithSameSignatureRationaleAndDeniedAct...
    method twoPermissionsWithSameSignatureFragment (line 208) | @Test public void twoPermissionsWithSameSignatureFragment() {
    method twoPermissionsWithSameSignatureAndRationaleFragment (line 212) | @Test public void twoPermissionsWithSameSignatureAndRationaleFragment() {
    method twoPermissionsWithSameSignatureAndDeniedFragment (line 216) | @Test public void twoPermissionsWithSameSignatureAndDeniedFragment() {
    method twoPermissionsWithSameSignatureRationaleAndDeniedFragment (line 220) | @Test public void twoPermissionsWithSameSignatureRationaleAndDeniedFra...
    method twoPermissionsAtOnceActivity (line 224) | @Test public void twoPermissionsAtOnceActivity() {
    method twoPermissionsFragment (line 228) | @Test public void twoPermissionsFragment() {
    method onePermissionWithNeverAskAndRationaleFragment (line 232) | @Test public void onePermissionWithNeverAskAndRationaleFragment() {
    method onePermissionWithNeverAskRationaleAndDeniedFragment (line 236) | @Test public void onePermissionWithNeverAskRationaleAndDeniedFragment() {
    method onePermissionWithNeverAskFragment (line 240) | @Test public void onePermissionWithNeverAskFragment() {
    method onePermissionWithNeverAskAndDeniedFragment (line 244) | @Test public void onePermissionWithNeverAskAndDeniedFragment() {
    method twoPermissionsWithTwoRationalesActivity (line 248) | @Test public void twoPermissionsWithTwoRationalesActivity() {
    method twoPermissionsWithTwoRationalesFragment (line 252) | @Test public void twoPermissionsWithTwoRationalesFragment() {
    method onePermissionWithOtherRationaleActivity (line 256) | @Test public void onePermissionWithOtherRationaleActivity() {
    method onePermissionWithOtherRationaleFragment (line 260) | @Test public void onePermissionWithOtherRationaleFragment() {
    method onePermissionWithOtherDeniedActivity (line 264) | @Test public void onePermissionWithOtherDeniedActivity() {
    method onePermissionWithOtherDeniedFragment (line 268) | @Test public void onePermissionWithOtherDeniedFragment() {
    method onePermissionWithRationaleAndDeniedActivity (line 272) | @Test public void onePermissionWithRationaleAndDeniedActivity() {
    method onePermissionWithRationaleAndDeniedFragment (line 276) | @Test public void onePermissionWithRationaleAndDeniedFragment() {
    method noDuplicatesDespiteRepeatedValuesActivity (line 280) | @Test public void noDuplicatesDespiteRepeatedValuesActivity() {
    method validMaxSdkVersion (line 285) | @Test public void validMaxSdkVersion() {
    method invalidMaxSdkVersion (line 289) | @Test public void invalidMaxSdkVersion() {
    method writeSettingsFragment (line 293) | @Test public void writeSettingsFragment() {
    method writeSettingsActivity (line 297) | @Test public void writeSettingsActivity() {
    method systemAlertWindowFragment (line 301) | @Test public void systemAlertWindowFragment() {
    method systemAlertWindowActivity (line 305) | @Test public void systemAlertWindowActivity() {
    method mixSystemAlertWindowAndNormalPermissionCompileError (line 309) | @Test public void mixSystemAlertWindowAndNormalPermissionCompileError() {
    method mixWriteSettingsAndNormalPermissionCompileError (line 314) | @Test public void mixWriteSettingsAndNormalPermissionCompileError() {
    method mixSystemAlertWindowAndWriteSettingsPermissionCompileError (line 319) | @Test public void mixSystemAlertWindowAndWriteSettingsPermissionCompil...
    method systemAlertWindowGenericsActivity (line 324) | @Test public void systemAlertWindowGenericsActivity() {
    method systemAlertWindowSupportGenericsFragment (line 328) | @Test public void systemAlertWindowSupportGenericsFragment() {
    method nestedActivity (line 332) | @Test public void nestedActivity() {
    method nestedStaticActivity (line 336) | @Test
    method nestedActivityWithDefaultPackage (line 341) | @Test
    method nestedFragment (line 346) | @Test
    method nestedStaticFragment (line 351) | @Test
    method onePermissionWithNoArgumentRationaleActivity (line 356) | @Test public void onePermissionWithNoArgumentRationaleActivity() {
    method onePermissionWithNoArgumentRationaleAndDeniedActivity (line 360) | @Test public void onePermissionWithNoArgumentRationaleAndDeniedActivit...
    method onePermissionWithParamNoArgumentRationaleAndDeniedActivity (line 364) | @Test public void onePermissionWithParamNoArgumentRationaleAndDeniedAc...
    method onePermissionWithNoArgumentRationaleFragment (line 368) | @Test public void onePermissionWithNoArgumentRationaleFragment() {
    method onePermissionWithNoArgumentRationaleAndDeniedFragment (line 372) | @Test public void onePermissionWithNoArgumentRationaleAndDeniedFragmen...
    method onePermissionWithParamNoArgumentRationaleAndDeniedFragment (line 376) | @Test public void onePermissionWithParamNoArgumentRationaleAndDeniedFr...

FILE: processor/src/test/java/permissions/dispatcher/processor/base/AndroidAwareClassLoader.java
  class AndroidAwareClassLoader (line 23) | final class AndroidAwareClassLoader {
    method AndroidAwareClassLoader (line 27) | private AndroidAwareClassLoader() {
    method create (line 31) | static ClassLoader create() {
    method unsafeToURL (line 46) | private static URL unsafeToURL(String spec) {

FILE: processor/src/test/java/permissions/dispatcher/processor/base/BaseTest.java
  class BaseTest (line 7) | public abstract class BaseTest {
    method actual (line 9) | final JavaFileObject actual() {
    method expect (line 13) | final JavaFileObject expect() {
    method getName (line 17) | protected abstract String getName();
    method getActualSource (line 18) | protected abstract String[] getActualSource();
    method getExpectSource (line 19) | protected abstract String[] getExpectSource();

FILE: processor/src/test/java/permissions/dispatcher/processor/base/StringEquals.java
  class StringEquals (line 5) | final class StringEquals extends SubstringMatcher {
    method StringEquals (line 7) | StringEquals(String substring) {
    method evalSubstringOf (line 11) | @Override
    method relationship (line 18) | @Override

FILE: processor/src/test/java/permissions/dispatcher/processor/base/TestSuite.java
  class TestSuite (line 11) | public abstract class TestSuite {
    method expectRuntimeException (line 19) | protected final void expectRuntimeException(final String message) {
    method assertJavaSource (line 24) | protected final void assertJavaSource(BaseTest test) {

FILE: processor/src/test/java/permissions/dispatcher/processor/data/Source.java
  class Source (line 5) | public final class Source {
    method Source (line 7) | private Source() {
    method getName (line 13) | @Override
    method getActualSource (line 18) | @Override
    method getExpectSource (line 31) | @Override
    method getName (line 38) | @Override
    method getActualSource (line 43) | @Override
    method getExpectSource (line 61) | @Override
    method getName (line 68) | @Override
    method getActualSource (line 73) | @Override
    method getExpectSource (line 96) | @Override
    method getName (line 103) | @Override
    method getActualSource (line 108) | @Override
    method getExpectSource (line 130) | @Override
    method getName (line 137) | @Override
    method getActualSource (line 142) | @Override
    method getExpectSource (line 164) | @Override
    method getName (line 171) | @Override
    method getActualSource (line 176) | @Override
    method getExpectSource (line 197) | @Override
    method getName (line 204) | @Override
    method getActualSource (line 209) | @Override
    method getExpectSource (line 231) | @Override
    method getName (line 238) | @Override
    method getActualSource (line 243) | @Override
    method getExpectSource (line 264) | @Override
    method getName (line 271) | @Override
    method getActualSource (line 276) | @Override
    method getExpectSource (line 297) | @Override
    method getName (line 304) | @Override
    method getActualSource (line 309) | @Override
    method getExpectSource (line 326) | @Override
    method getName (line 333) | @Override
    method getActualSource (line 338) | @Override
    method getExpectSource (line 360) | @Override
    method getName (line 367) | @Override
    method getActualSource (line 372) | @Override
    method getExpectSource (line 393) | @Override
    method getName (line 400) | @Override
    method getActualSource (line 405) | @Override
    method getExpectSource (line 426) | @Override
    method getName (line 433) | @Override
    method getActualSource (line 438) | @Override
    method getExpectSource (line 455) | @Override
    method getName (line 462) | @Override
    method getActualSource (line 467) | @Override
    method getExpectSource (line 489) | @Override
    method getName (line 496) | @Override
    method getActualSource (line 501) | @Override
    method getExpectSource (line 522) | @Override
    method getName (line 529) | @Override
    method getActualSource (line 534) | @Override
    method getExpectSource (line 555) | @Override
    method getName (line 562) | @Override
    method getActualSource (line 567) | @Override
    method getExpectSource (line 584) | @Override
    method getName (line 591) | @Override
    method getActualSource (line 596) | @Override
    method getExpectSource (line 621) | @Override
    method getName (line 628) | @Override
    method getActualSource (line 633) | @Override
    method getExpectSource (line 657) | @Override
    method getName (line 664) | @Override
    method getActualSource (line 669) | @Override
    method getExpectSource (line 693) | @Override
    method getName (line 700) | @Override
    method getActualSource (line 705) | @Override
    method getExpectSource (line 722) | @Override
    method getName (line 759) | @Override
    method getActualSource (line 764) | @Override
    method getExpectSource (line 781) | @Override
    method getName (line 817) | @Override
    method getActualSource (line 822) | @Override
    method getExpectSource (line 839) | @Override
    method getName (line 907) | @Override
    method getActualSource (line 912) | @Override
    method getExpectSource (line 934) | @Override
    method getName (line 1006) | @Override
    method getActualSource (line 1011) | @Override
    method getExpectSource (line 1033) | @Override
    method getName (line 1106) | @Override
    method getActualSource (line 1111) | @Override
    method getExpectSource (line 1137) | @Override
    method getName (line 1214) | @Override
    method getActualSource (line 1219) | @Override
    method getExpectSource (line 1240) | @Override
    method getName (line 1281) | @Override
    method getActualSource (line 1286) | @Override
    method getExpectSource (line 1312) | @Override
    method getName (line 1375) | @Override
    method getActualSource (line 1380) | @Override
    method getExpectSource (line 1410) | @Override
    method getName (line 1478) | @Override
    method getActualSource (line 1483) | @Override
    method getExpectSource (line 1508) | @Override
    method getName (line 1551) | @Override
    method getActualSource (line 1556) | @Override
    method getExpectSource (line 1583) | @Override
    method getName (line 1642) | @Override
    method getActualSource (line 1647) | @Override
    method getExpectSource (line 1667) | @Override
    method getName (line 1718) | @Override
    method getActualSource (line 1723) | @Override
    method getExpectSource (line 1740) | @Override
    method getName (line 1777) | @Override
    method getActualSource (line 1782) | @Override
    method getExpectSource (line 1802) | @Override
    method getName (line 1852) | @Override
    method getActualSource (line 1857) | @Override
    method getExpectSource (line 1877) | @Override
    method getName (line 1928) | @Override
    method getActualSource (line 1933) | @Override
    method getExpectSource (line 1958) | @Override
    method getName (line 2050) | @Override
    method getActualSource (line 2055) | @Override
    method getExpectSource (line 2079) | @Override
    method getName (line 2134) | @Override
    method getActualSource (line 2139) | @Override
    method getExpectSource (line 2168) | @Override
    method getName (line 2270) | @Override
    method getActualSource (line 2275) | @Override
    method getExpectSource (line 2301) | @Override
    method getName (line 2363) | @Override
    method getActualSource (line 2368) | @Override
    method getExpectSource (line 2398) | @Override
    method getName (line 2465) | @Override
    method getActualSource (line 2470) | @Override
    method getExpectSource (line 2491) | @Override
    method getName (line 2531) | @Override
    method getActualSource (line 2536) | @Override
    method getExpectSource (line 2561) | @Override
    method getName (line 2603) | @Override
    method getActualSource (line 2608) | @Override
    method getExpectSource (line 2626) | @Override
    method getName (line 2695) | @Override
    method getActualSource (line 2700) | @Override
    method getExpectSource (line 2722) | @Override
    method getName (line 2795) | @Override
    method getActualSource (line 2800) | @Override
    method getExpectSource (line 2821) | @Override
    method getName (line 2895) | @Override
    method getActualSource (line 2900) | @Override
    method getExpectSource (line 2926) | @Override
    method getName (line 3004) | @Override
    method getActualSource (line 3009) | @Override
    method getExpectSource (line 3030) | @Override
    method getName (line 3080) | @Override
    method getActualSource (line 3085) | @Override
    method getExpectSource (line 3110) | @Override
    method getName (line 3201) | @Override
    method getActualSource (line 3206) | @Override
    method getExpectSource (line 3230) | @Override
    method getName (line 3284) | @Override
    method getActualSource (line 3289) | @Override
    method getExpectSource (line 3318) | @Override
    method getName (line 3419) | @Override
    method getActualSource (line 3424) | @Override
    method getExpectSource (line 3452) | @Override
    method getName (line 3544) | @Override
    method getActualSource (line 3549) | @Override
    method getExpectSource (line 3577) | @Override
    method getName (line 3668) | @Override
    method getActualSource (line 3673) | @Override
    method getExpectSource (line 3695) | @Override
    method getName (line 3732) | @Override
    method getActualSource (line 3737) | @Override
    method getExpectSource (line 3759) | @Override
    method getName (line 3795) | @Override
    method getActualSource (line 3800) | @Override
    method getExpectSource (line 3821) | @Override
    method getName (line 3858) | @Override
    method getActualSource (line 3863) | @Override
    method getExpectSource (line 3884) | @Override
    method getName (line 3920) | @Override
    method getActualSource (line 3925) | @Override
    method getExpectSource (line 3951) | @Override
    method getName (line 4015) | @Override
    method getActualSource (line 4020) | @Override
    method getExpectSource (line 4046) | @Override
    method getName (line 4109) | @Override
    method getActualSource (line 4114) | @Override
    method getExpectSource (line 4148) | @Override
    method getName (line 4211) | @Override
    method getActualSource (line 4216) | @Override
    method getExpectSource (line 4241) | @Override
    method getName (line 4289) | @Override
    method getActualSource (line 4294) | @Override
    method getExpectSource (line 4319) | @Override
    method getName (line 4362) | @Override
    method getActualSource (line 4367) | @Override
    method getExpectSource (line 4394) | @Override
    method getName (line 4462) | @Override
    method getActualSource (line 4467) | @Override
    method getExpectSource (line 4494) | @Override
    method getName (line 4562) | @Override
    method getActualSource (line 4567) | @Override
    method getExpectSource (line 4594) | @Override
    method getName (line 4662) | @Override
    method getActualSource (line 4667) | @Override
    method getExpectSource (line 4694) | @Override
    method getName (line 4762) | @Override
    method getActualSource (line 4767) | @Override
    method getExpectSource (line 4796) | @Override
    method getName (line 4867) | @Override
    method getActualSource (line 4872) | @Override
    method getExpectSource (line 4901) | @Override
    method getName (line 4972) | @Override
    method getActualSource (line 4977) | @Override
    method getExpectSource (line 4994) | @Override
    method getName (line 5001) | @Override
    method getActualSource (line 5006) | @Override
    method getExpectSource (line 5023) | @Override
    method getName (line 5030) | @Override
    method getActualSource (line 5035) | @Override
    method getExpectSource (line 5052) | @Override
    method getName (line 5059) | @Override
    method getActualSource (line 5064) | @Override
    method getExpectSource (line 5085) | @Override
    method getName (line 5122) | @Override
    method getActualSource (line 5127) | @Override
    method getExpectSource (line 5146) | @Override
    method getName (line 5183) | @Override
    method getActualSource (line 5188) | @Override
    method getExpectSource (line 5208) | @Override
    method getName (line 5244) | @Override
    method getActualSource (line 5249) | @Override
    method getExpectSource (line 5268) | @Override
    method getName (line 5304) | @Override
    method getActualSource (line 5309) | @Override
    method getExpectSource (line 5328) | @Override
    method getName (line 5364) | @Override
    method getActualSource (line 5369) | @Override
    method getExpectSource (line 5390) | @Override
    method getName (line 5397) | @Override
    method getActualSource (line 5402) | @Override
    method getExpectSource (line 5432) | @Override
    method getName (line 5439) | @Override
    method getActualSource (line 5444) | @Override
    method getExpectSource (line 5474) | @Override
    method getName (line 5481) | @Override
    method getActualSource (line 5486) | @Override
    method getExpectSource (line 5507) | @Override
    method getName (line 5551) | @Override
    method getActualSource (line 5556) | @Override
    method getExpectSource (line 5581) | @Override
    method getName (line 5630) | @Override
    method getActualSource (line 5635) | @Override
    method getExpectSource (line 5660) | @Override
    method getName (line 5712) | @Override
    method getActualSource (line 5717) | @Override
    method getExpectSource (line 5738) | @Override
    method getName (line 5781) | @Override
    method getActualSource (line 5786) | @Override
    method getExpectSource (line 5811) | @Override
    method getName (line 5858) | @Override
    method getActualSource (line 5863) | @Override
    method getExpectSource (line 5888) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityOnlyNeedsPermission.java
  class ActivityOnlyNeedsPermission (line 10) | @RuntimePermissions
    method showCamera (line 13) | @NeedsPermission(Manifest.permission.CAMERA)
    method onRequestPermissionsResult (line 17) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityWithAllAnnotations.java
  class ActivityWithAllAnnotations (line 14) | @RuntimePermissions
    method showCamera (line 17) | @NeedsPermission(Manifest.permission.CAMERA)
    method showRationaleForCamera (line 21) | @OnShowRationale(Manifest.permission.CAMERA)
    method showDeniedForCamera (line 25) | @OnPermissionDenied(Manifest.permission.CAMERA)
    method showNeverAskForCamera (line 29) | @OnNeverAskAgain(Manifest.permission.CAMERA)
    method onRequestPermissionsResult (line 33) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityWithOnNeverAskAgain.java
  class ActivityWithOnNeverAskAgain (line 11) | @RuntimePermissions
    method showCamera (line 13) | @NeedsPermission(Manifest.permission.CAMERA)
    method showNeverAskForCamera (line 17) | @OnNeverAskAgain(Manifest.permission.CAMERA)
    method onRequestPermissionsResult (line 21) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityWithOnPermissionDenied.java
  class ActivityWithOnPermissionDenied (line 11) | @RuntimePermissions
    method showCamera (line 13) | @NeedsPermission(Manifest.permission.CAMERA)
    method showDeniedForCamera (line 17) | @OnPermissionDenied(Manifest.permission.CAMERA)
    method onRequestPermissionsResult (line 21) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityWithShowRationale.java
  class ActivityWithShowRationale (line 12) | @RuntimePermissions
    method showCamera (line 15) | @NeedsPermission(Manifest.permission.CAMERA)
    method showRationaleForCamera (line 19) | @OnShowRationale(Manifest.permission.CAMERA)
    method onRequestPermissionsResult (line 23) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityWithSystemAlertWindow.java
  class ActivityWithSystemAlertWindow (line 10) | @RuntimePermissions
    method systemAlertWindow (line 13) | @NeedsPermission(Manifest.permission.SYSTEM_ALERT_WINDOW)
    method onActivityResult (line 17) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityWithSystemAlertWindowAllAnnotations.java
  class ActivityWithSystemAlertWindowAllAnnotations (line 13) | @RuntimePermissions
    method systemAlertWindow (line 16) | @NeedsPermission(Manifest.permission.SYSTEM_ALERT_WINDOW)
    method showRationaleForSystemAlertWindow (line 20) | @OnShowRationale(Manifest.permission.SYSTEM_ALERT_WINDOW)
    method showDeniedForSystemAlertWindow (line 24) | @OnPermissionDenied(Manifest.permission.SYSTEM_ALERT_WINDOW)
    method onActivityResult (line 28) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityWithWriteSetting.java
  class ActivityWithWriteSetting (line 10) | @RuntimePermissions
    method writeSetting (line 13) | @NeedsPermission(Manifest.permission.WRITE_SETTINGS)
    method onActivityResult (line 17) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/ActivityWithWriteSettingAllAnnotations.java
  class ActivityWithWriteSettingAllAnnotations (line 13) | @RuntimePermissions
    method writeSetting (line 16) | @NeedsPermission(Manifest.permission.WRITE_SETTINGS)
    method showRationaleForWriteSettings (line 20) | @OnShowRationale(Manifest.permission.WRITE_SETTINGS)
    method showDeniedForWriteSettings (line 24) | @OnPermissionDenied(Manifest.permission.WRITE_SETTINGS)
    method onActivityResult (line 28) | @Override

FILE: test/src/main/java/permissions/dispatcher/test/FragmentWithAllAnnotations.java
  class FragmentWithAllAnnotations (line 14) | @RuntimePermissions
    method showCamera (line 17) | @NeedsPermission(Manifest.permission.CAMERA)
    method showRationaleForCamera (line 21) | @OnShowRationale(Manifest.permission.CAMERA)
    method showDeniedForCamera (line 25) | @OnPermissionDenied(Manifest.permission.CAMERA)
    method showNeverAskForCamera (line 29) | @OnNeverAskAgain(Manifest.permission.CAMERA)
    method onRequestPermissionsResult (line 33) | @Override
Condensed preview — 185 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (811K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 1106,
    "preview": "## FAQs\n\n<!--\nBefore you add the issue, please make sure to read over the following Frequently Asked Questions:\n\n**\"XxxP"
  },
  {
    "path": ".github/workflows/deploy_snapshot.yml",
    "chars": 941,
    "preview": "name: Deploy snapshot\n\non:\n  pull_request:\n    branches:\n      - master\n    types: [closed]\n\njobs:\n  build:\n    runs-on:"
  },
  {
    "path": ".github/workflows/ktx-release.yml",
    "chars": 1164,
    "preview": "name: KTX Release\n\non:\n  push:\n    tags:\n      - 'ktx-*.*.*'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    -"
  },
  {
    "path": ".github/workflows/pull_request_ci.yml",
    "chars": 546,
    "preview": "name: CI for pull request\n\non:\n  pull_request:\n    types: [assigned, opened, synchronize, reopened]\n\njobs:\n  build:\n    "
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 1167,
    "preview": "name: Release\n\non:\n  push:\n    tags:\n      - '[0-9]+.[0-9]+.[0-9]+'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps"
  },
  {
    "path": ".gitignore",
    "chars": 47,
    "preview": ".gradle\nlocal.properties\n\nbuild/\n\n.idea/\n*.iml\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 12882,
    "preview": "# ChangeLog\n\n- 4.9.2 | ktx: 1.1.4 2022/04/04\n  - Fix: [fix: use ContextCompat instead of PermissionChecker](https://gith"
  },
  {
    "path": "LICENSE",
    "chars": 11356,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 6642,
    "preview": "# PermissionsDispatcher ![CI for pull request](https://github.com/permissions-dispatcher/PermissionsDispatcher/workflows"
  },
  {
    "path": "annotation/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "annotation/build.gradle",
    "chars": 74,
    "preview": "apply plugin: 'java-library'\napply plugin: \"com.vanniktech.maven.publish\"\n"
  },
  {
    "path": "annotation/gradle.properties",
    "chars": 124,
    "preview": "POM_NAME        = permissionsdispatcher-annotation\nPOM_ARTIFACT_ID = permissionsdispatcher-annotation\nPOM_PACKAGING   = "
  },
  {
    "path": "annotation/src/main/java/permissions/dispatcher/GrantableRequest.java",
    "chars": 115,
    "preview": "package permissions.dispatcher;\n\npublic interface GrantableRequest extends PermissionRequest {\n    void grant();\n}\n"
  },
  {
    "path": "annotation/src/main/java/permissions/dispatcher/NeedsPermission.java",
    "chars": 415,
    "preview": "package permissions.dispatcher;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport "
  },
  {
    "path": "annotation/src/main/java/permissions/dispatcher/OnNeverAskAgain.java",
    "chars": 410,
    "preview": "package permissions.dispatcher;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport "
  },
  {
    "path": "annotation/src/main/java/permissions/dispatcher/OnPermissionDenied.java",
    "chars": 382,
    "preview": "package permissions.dispatcher;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport "
  },
  {
    "path": "annotation/src/main/java/permissions/dispatcher/OnShowRationale.java",
    "chars": 391,
    "preview": "package permissions.dispatcher;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport "
  },
  {
    "path": "annotation/src/main/java/permissions/dispatcher/PermissionRequest.java",
    "chars": 243,
    "preview": "package permissions.dispatcher;\n\n/**\n * Interface used by {@link OnShowRationale} methods to allow for continuation\n * o"
  },
  {
    "path": "annotation/src/main/java/permissions/dispatcher/RuntimePermissions.java",
    "chars": 388,
    "preview": "package permissions.dispatcher;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport "
  },
  {
    "path": "build.gradle",
    "chars": 655,
    "preview": "buildscript {\n    repositories {\n        mavenCentral()\n        google()\n        jcenter()\n    }\n    dependencies {\n    "
  },
  {
    "path": "buildSrc/build.gradle",
    "chars": 189,
    "preview": "apply plugin: \"groovy\"\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation gradleApi()\n    implement"
  },
  {
    "path": "buildSrc/src/main/groovy/permissions/dispatcher/AarToJarDependencyPlugin.groovy",
    "chars": 3861,
    "preview": "package permissions.dispatcher\n\nimport org.apache.commons.io.FileUtils\nimport org.gradle.api.DefaultTask\nimport org.grad"
  },
  {
    "path": "buildSrc/src/main/groovy/permissions/dispatcher/AndroidJarDependencyExtension.groovy",
    "chars": 141,
    "preview": "package permissions.dispatcher\n\nimport javax.annotation.Nullable\n\nclass AndroidJarDependencyExtension {\n    @Nullable\n  "
  },
  {
    "path": "buildSrc/src/main/groovy/permissions/dispatcher/AndroidJarDependencyPlugin.groovy",
    "chars": 2272,
    "preview": "package permissions.dispatcher\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\n/**\n * This plugin creates a"
  },
  {
    "path": "doc/java_usage.md",
    "chars": 3792,
    "preview": "## Usage with Java\n\nHere's a minimum example, in which you register a `MainActivity` which requires `Manifest.permission"
  },
  {
    "path": "doc/maxsdkversion.md",
    "chars": 785,
    "preview": "## maxSdkVersion\n\n[&lt;uses-permission&gt;](https://developer.android.com/guide/topics/manifest/uses-permission-element."
  },
  {
    "path": "doc/migration_guide.md",
    "chars": 7810,
    "preview": "# Migration guide\n\n- [Migrating to Maven Central](#migrating-to-maven-central)\n- [Migrating to 4.x](#migrating-to-4x)\n- "
  },
  {
    "path": "doc/special_permissions.md",
    "chars": 1723,
    "preview": "## Special Permissions\n\nPermissionsDispatcher takes care of special permissions `Manifest.permission.SYSTEM_ALERT_WINDOW"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 202,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "gradle.properties",
    "chars": 1925,
    "preview": "# Upload configuration\nUSER                    = hotchemi\nGROUP                   = com.github.permissions-dispatcher\nVE"
  },
  {
    "path": "gradlew",
    "chars": 5296,
    "preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up"
  },
  {
    "path": "gradlew.bat",
    "chars": 2260,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
  },
  {
    "path": "ktx/README.md",
    "chars": 5500,
    "preview": "## permissionsdispatcher-ktx\n\n`permissionsdispatcher-ktx` aims to let developers cope with runtime permissions handling "
  },
  {
    "path": "ktx/build.gradle",
    "chars": 1347,
    "preview": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'org.jetbrains.dokka'\napply plugin: \"co"
  },
  {
    "path": "ktx/gradle.properties",
    "chars": 99,
    "preview": "POM_NAME        = ktx\nPOM_ARTIFACT_ID = ktx\nPOM_PACKAGING   = aar\nVERSION_NAME    = 1.2.0-SNAPSHOT\n"
  },
  {
    "path": "ktx/src/main/AndroidManifest.xml",
    "chars": 49,
    "preview": "<manifest package=\"permissions.dispatcher.ktx\"/>\n"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/ActivityExtensions.kt",
    "chars": 4435,
    "preview": "package permissions.dispatcher.ktx\n\nimport android.annotation.SuppressLint\nimport android.provider.Settings\nimport andro"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/Event.kt",
    "chars": 625,
    "preview": "package permissions.dispatcher.ktx\n\nimport androidx.annotation.AnyThread\n\n/**\n * Used as a wrapper for data that is expo"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/FragmentExtensions.kt",
    "chars": 4447,
    "preview": "package permissions.dispatcher.ktx\n\nimport android.annotation.SuppressLint\nimport android.provider.Settings\nimport andro"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/KtxPermissionRequest.kt",
    "chars": 722,
    "preview": "package permissions.dispatcher.ktx\n\nimport permissions.dispatcher.PermissionRequest\nimport java.lang.ref.WeakReference\n\n"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/LocationPermission.kt",
    "chars": 789,
    "preview": "package permissions.dispatcher.ktx\n\nimport android.Manifest\nimport android.annotation.SuppressLint\nimport android.os.Bui"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/PermissionRequestFragment.kt",
    "chars": 4195,
    "preview": "package permissions.dispatcher.ktx\n\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\n"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/PermissionRequestResult.kt",
    "chars": 126,
    "preview": "package permissions.dispatcher.ktx\n\ninternal enum class PermissionResult {\n    GRANTED,\n    DENIED,\n    DENIED_AND_DISAB"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/PermissionRequestType.kt",
    "chars": 1971,
    "preview": "package permissions.dispatcher.ktx\n\nimport android.content.Context\nimport android.os.Build\nimport android.provider.Setti"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/PermissionRequestViewModel.kt",
    "chars": 1555,
    "preview": "package permissions.dispatcher.ktx\n\nimport androidx.annotation.MainThread\nimport androidx.lifecycle.LifecycleOwner\nimpor"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/PermissionsRequester.kt",
    "chars": 237,
    "preview": "package permissions.dispatcher.ktx\n\n/**\n * An intermediate class that is able to launch permissions request process as a"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/PermissionsRequesterImpl.kt",
    "chars": 1830,
    "preview": "package permissions.dispatcher.ktx\n\nimport androidx.fragment.app.FragmentActivity\nimport androidx.lifecycle.ViewModelPro"
  },
  {
    "path": "ktx/src/main/java/permissions/dispatcher/ktx/TypeAliases.kt",
    "chars": 187,
    "preview": "package permissions.dispatcher.ktx\n\nimport permissions.dispatcher.PermissionRequest\n\ninternal typealias Fun = () -> Unit"
  },
  {
    "path": "ktx/src/test/java/permissions/dispatcher/test/EventTest.kt",
    "chars": 508,
    "preview": "package permissions.dispatcher.test\n\nimport junit.framework.Assert.assertEquals\nimport junit.framework.Assert.assertNull"
  },
  {
    "path": "ktx/src/test/java/permissions/dispatcher/test/KtxPermissionRequestTest.kt",
    "chars": 1071,
    "preview": "package permissions.dispatcher.test\n\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.verify\ni"
  },
  {
    "path": "ktx/src/test/java/permissions/dispatcher/test/LocationPermissionTest.kt",
    "chars": 1275,
    "preview": "package permissions.dispatcher.test\n\nimport org.junit.Assert.*\nimport org.junit.Test\nimport permissions.dispatcher.ktx.L"
  },
  {
    "path": "ktx/src/test/java/permissions/dispatcher/test/PermissionRequestViewModelTest.kt",
    "chars": 3230,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport androidx.arch.core.executor.testing.InstantTaskExecu"
  },
  {
    "path": "ktx/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "chars": 17,
    "preview": "mock-maker-inline"
  },
  {
    "path": "ktx-sample/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "ktx-sample/build.gradle",
    "chars": 675,
    "preview": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\n\nandroid {\n    compileSdkVersion COMPILE_SDK_VERS"
  },
  {
    "path": "ktx-sample/src/main/AndroidManifest.xml",
    "chars": 942,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "ktx-sample/src/main/java/permissions/dispatcher/ktx/sample/MainActivity.kt",
    "chars": 560,
    "preview": "package permissions.dispatcher.ktx.sample\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\n\ncla"
  },
  {
    "path": "ktx-sample/src/main/java/permissions/dispatcher/ktx/sample/MainFragment.kt",
    "chars": 3463,
    "preview": "package permissions.dispatcher.ktx.sample\n\nimport android.Manifest\nimport android.content.Context\nimport android.content"
  },
  {
    "path": "ktx-sample/src/main/res/layout/activity_main.xml",
    "chars": 273,
    "preview": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tool"
  },
  {
    "path": "ktx-sample/src/main/res/layout/fragment_camera.xml",
    "chars": 604,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    and"
  },
  {
    "path": "ktx-sample/src/main/res/layout/fragment_main.xml",
    "chars": 902,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/too"
  },
  {
    "path": "ktx-sample/src/main/res/values/dimens.xml",
    "chars": 280,
    "preview": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"margin_medium\">16dp"
  },
  {
    "path": "ktx-sample/src/main/res/values/strings.xml",
    "chars": 881,
    "preview": "<resources>\n    <string name=\"app_name\">PermissionsDispatcher Sample</string>\n    <string name=\"back\">Back</string>\n    "
  },
  {
    "path": "ktx-sample/src/main/res/values/template-styles.xml",
    "chars": 100,
    "preview": "<resources>\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\" />\n</resources>\n"
  },
  {
    "path": "library/build.gradle",
    "chars": 1530,
    "preview": "apply plugin: 'com.android.library'\napply plugin: \"com.vanniktech.maven.publish\"\n\nandroid {\n    compileSdkVersion COMPIL"
  },
  {
    "path": "library/gradle.properties",
    "chars": 102,
    "preview": "POM_NAME        = permissionsdispatcher\nPOM_ARTIFACT_ID = permissionsdispatcher\nPOM_PACKAGING   = aar\n"
  },
  {
    "path": "library/src/main/AndroidManifest.xml",
    "chars": 45,
    "preview": "<manifest package=\"permissions.dispatcher\"/>\n"
  },
  {
    "path": "library/src/main/java/permissions/dispatcher/PermissionUtils.java",
    "chars": 5503,
    "preview": "package permissions.dispatcher;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.pm."
  },
  {
    "path": "library/src/test/java/permissions/dispatcher/ApiLevelTestSuite.java",
    "chars": 7128,
    "preview": "package permissions.dispatcher;\n\nimport android.Manifest;\nimport android.annotation.SuppressLint;\nimport android.content"
  },
  {
    "path": "lint/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "lint/build.gradle",
    "chars": 881,
    "preview": "apply plugin: 'java'\napply plugin: 'kotlin'\n\ntargetCompatibility = JavaVersion.VERSION_1_6\nsourceCompatibility = JavaVer"
  },
  {
    "path": "lint/src/main/java/permissions/dispatcher/CallNeedsPermissionDetector.java",
    "chars": 5927,
    "preview": "package permissions.dispatcher;\n\nimport com.android.tools.lint.client.api.UElementHandler;\nimport com.android.tools.lint"
  },
  {
    "path": "lint/src/main/java/permissions/dispatcher/CallOnRequestPermissionsResultDetector.java",
    "chars": 7041,
    "preview": "package permissions.dispatcher;\n\nimport androidx.annotation.Nullable;\n\nimport com.android.tools.lint.client.api.UElement"
  },
  {
    "path": "lint/src/main/java/permissions/dispatcher/NoCorrespondingNeedsPermissionDetector.java",
    "chars": 4510,
    "preview": "package permissions.dispatcher;\n\nimport com.android.tools.lint.client.api.UElementHandler;\nimport com.android.tools.lint"
  },
  {
    "path": "lint/src/main/java/permissions/dispatcher/NoDelegateOnResumeDetector.java",
    "chars": 5892,
    "preview": "package permissions.dispatcher;\n\nimport com.android.tools.lint.client.api.UElementHandler;\nimport com.android.tools.lint"
  },
  {
    "path": "lint/src/main/java/permissions/dispatcher/PermissionsDispatcherIssueRegistry.java",
    "chars": 727,
    "preview": "package permissions.dispatcher;\n\nimport com.android.tools.lint.client.api.IssueRegistry;\nimport com.android.tools.lint.d"
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/CallNeedsPermissionDetectorKtTest.kt",
    "chars": 5042,
    "preview": "package permissions.dispatcher\n\nimport com.android.tools.lint.checks.infrastructure.TestFiles.java\nimport com.android.to"
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/CallNeedsPermissionDetectorTest.kt",
    "chars": 5184,
    "preview": "package permissions.dispatcher\n\nimport com.android.tools.lint.checks.infrastructure.TestFiles.java\nimport com.android.to"
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/CallOnRequestPermissionsResultDetectorKtTest.kt",
    "chars": 6861,
    "preview": "package permissions.dispatcher\n\nimport com.android.tools.lint.checks.infrastructure.TestFiles.java\nimport com.android.to"
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/CallOnRequestPermissionsResultDetectorTest.kt",
    "chars": 3787,
    "preview": "package permissions.dispatcher\n\nimport org.intellij.lang.annotations.Language\nimport org.junit.Test\n\nimport com.android."
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/NoCorrespondingNeedsPermissionDetectorKtTest.kt",
    "chars": 3104,
    "preview": "package permissions.dispatcher\n\nimport org.intellij.lang.annotations.Language\nimport org.junit.Test\n\nimport com.android."
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/NoCorrespondingNeedsPermissionDetectorTest.kt",
    "chars": 3068,
    "preview": "package permissions.dispatcher\n\nimport org.intellij.lang.annotations.Language\nimport org.junit.Test\n\nimport com.android."
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/NoDelegateOnResumeDetectorKtTest.kt",
    "chars": 7394,
    "preview": "package permissions.dispatcher\n\nimport com.android.tools.lint.checks.infrastructure.TestFiles\nimport com.android.tools.l"
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/NoDelegateOnResumeDetectorTest.kt",
    "chars": 7326,
    "preview": "package permissions.dispatcher\n\nimport com.android.tools.lint.checks.infrastructure.TestFiles\nimport com.android.tools.l"
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/PermissionsDispatcherIssueRegistryTest.kt",
    "chars": 442,
    "preview": "package permissions.dispatcher\n\nimport org.junit.Test\n\nimport com.google.common.truth.Truth.assertThat\n\nclass Permission"
  },
  {
    "path": "lint/src/test/java/permissions/dispatcher/Utils.kt",
    "chars": 1842,
    "preview": "package permissions.dispatcher\n\ninternal object Utils {\n\n    val runtimePermission = \"\"\"\n                 |package permi"
  },
  {
    "path": "processor/build.gradle",
    "chars": 1857,
    "preview": "import org.gradle.internal.jvm.Jvm\nimport permissions.dispatcher.AndroidJarDependencyPlugin\nimport permissions.dispatche"
  },
  {
    "path": "processor/gradle.properties",
    "chars": 122,
    "preview": "POM_NAME        = permissionsdispatcher-processor\nPOM_ARTIFACT_ID = permissionsdispatcher-processor\nPOM_PACKAGING   = ja"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/PermissionsProcessor.kt",
    "chars": 3613,
    "preview": "package permissions.dispatcher.processor\n\nimport permissions.dispatcher.RuntimePermissions\nimport permissions.dispatcher"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/ProcessorUnit.kt",
    "chars": 604,
    "preview": "package permissions.dispatcher.processor\n\nimport com.squareup.javapoet.JavaFile\nimport com.squareup.kotlinpoet.FileSpec\n"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/RequestCodeProvider.kt",
    "chars": 496,
    "preview": "package permissions.dispatcher.processor\n\nimport java.util.concurrent.atomic.AtomicInteger\n\n/**\n * Helper class providin"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/RuntimePermissionsElement.kt",
    "chars": 3585,
    "preview": "package permissions.dispatcher.processor\n\nimport com.squareup.javapoet.TypeName\nimport com.squareup.javapoet.TypeVariabl"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/DuplicatedMethodNameException.kt",
    "chars": 377,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.util.simpleString\nimport jav"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/DuplicatedValueException.kt",
    "chars": 369,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.util.simpleString\nimport jav"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/MixPermissionTypeException.kt",
    "chars": 359,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.util.simpleString\nimport jav"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/NoAnnotatedMethodsException.kt",
    "chars": 323,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.RuntimePermissionsElement\n\nc"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/NoParametersAllowedException.kt",
    "chars": 299,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.util.simpleString\nimport jav"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/NoThrowsAllowedException.kt",
    "chars": 322,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.util.simpleString\nimport jav"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/PrivateMethodException.kt",
    "chars": 357,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.util.simpleString\nimport jav"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/SpecialPermissionsWithNeverAskAgainException.kt",
    "chars": 236,
    "preview": "package permissions.dispatcher.processor.exception\n\nclass SpecialPermissionsWithNeverAskAgainException : RuntimeExceptio"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/WrongClassException.kt",
    "chars": 272,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport com.squareup.javapoet.TypeName\nimport javax.lang.model.type.T"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/WrongParametersException.kt",
    "chars": 471,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.util.simpleString\nimport jav"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/exception/WrongReturnTypeException.kt",
    "chars": 321,
    "preview": "package permissions.dispatcher.processor.exception\n\nimport permissions.dispatcher.processor.util.simpleString\nimport jav"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/java/JavaActivityProcessorUnit.kt",
    "chars": 1198,
    "preview": "package permissions.dispatcher.processor.impl.java\n\nimport com.squareup.javapoet.ClassName\nimport com.squareup.javapoet."
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/java/JavaBaseProcessorUnit.kt",
    "chars": 26791,
    "preview": "package permissions.dispatcher.processor.impl.java\n\nimport androidx.annotation.NonNull\nimport com.squareup.javapoet.Arra"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/java/JavaFragmentProcessorUnit.kt",
    "chars": 1131,
    "preview": "package permissions.dispatcher.processor.impl.java\n\nimport com.squareup.javapoet.MethodSpec\nimport permissions.dispatche"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/java/SensitivePermissionInterface.kt",
    "chars": 386,
    "preview": "package permissions.dispatcher.processor.impl.java\n\nimport com.squareup.javapoet.MethodSpec\n\ninterface SensitivePermissi"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/java/SystemAlertWindowHelper.kt",
    "chars": 1233,
    "preview": "package permissions.dispatcher.processor.impl.java\n\nimport com.squareup.javapoet.ClassName\nimport com.squareup.javapoet."
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/java/WriteSettingsHelper.kt",
    "chars": 1224,
    "preview": "package permissions.dispatcher.processor.impl.java\n\nimport com.squareup.javapoet.ClassName\nimport com.squareup.javapoet."
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/kotlin/KotlinActivityProcessorUnit.kt",
    "chars": 1241,
    "preview": "package permissions.dispatcher.processor.impl.kotlin\n\nimport com.squareup.kotlinpoet.ClassName\nimport com.squareup.kotli"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/kotlin/KotlinBaseProcessorUnit.kt",
    "chars": 24362,
    "preview": "package permissions.dispatcher.processor.impl.kotlin\n\nimport com.squareup.kotlinpoet.*\nimport com.squareup.kotlinpoet.Pa"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/kotlin/KotlinFragmentProcessorUnit.kt",
    "chars": 1185,
    "preview": "package permissions.dispatcher.processor.impl.kotlin\n\nimport com.squareup.kotlinpoet.FunSpec\nimport permissions.dispatch"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/kotlin/SensitivePermissionInterface.kt",
    "chars": 388,
    "preview": "package permissions.dispatcher.processor.impl.kotlin\n\nimport com.squareup.kotlinpoet.FunSpec\n\ninterface SensitivePermiss"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/kotlin/SystemAlertWindowHelper.kt",
    "chars": 1182,
    "preview": "package permissions.dispatcher.processor.impl.kotlin\n\nimport com.squareup.kotlinpoet.ClassName\nimport com.squareup.kotli"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/impl/kotlin/WriteSettingsHelper.kt",
    "chars": 1174,
    "preview": "package permissions.dispatcher.processor.impl.kotlin\n\nimport com.squareup.kotlinpoet.ClassName\nimport com.squareup.kotli"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/util/Constants.kt",
    "chars": 458,
    "preview": "package permissions.dispatcher.processor.util\n\nconst val FILE_COMMENT = \"This file was generated by PermissionsDispatche"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/util/Extensions.kt",
    "chars": 5423,
    "preview": "package permissions.dispatcher.processor.util\n\nimport com.squareup.kotlinpoet.*\nimport com.squareup.kotlinpoet.Parameter"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/util/Helpers.kt",
    "chars": 4054,
    "preview": "package permissions.dispatcher.processor.util\n\nimport com.squareup.javapoet.CodeBlock\nimport com.squareup.javapoet.TypeN"
  },
  {
    "path": "processor/src/main/kotlin/permissions/dispatcher/processor/util/Validators.kt",
    "chars": 5132,
    "preview": "package permissions.dispatcher.processor.util\n\nimport permissions.dispatcher.OnNeverAskAgain\nimport permissions.dispatch"
  },
  {
    "path": "processor/src/main/resources/META-INF/gradle/incremental.annotation.processors",
    "chars": 63,
    "preview": "permissions.dispatcher.processor.PermissionsProcessor,isolating"
  },
  {
    "path": "processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor",
    "chars": 53,
    "preview": "permissions.dispatcher.processor.PermissionsProcessor"
  },
  {
    "path": "processor/src/test/java/permissions/dispatcher/processor/KtProcessorTestSuite.kt",
    "chars": 24604,
    "preview": "package permissions.dispatcher.processor\n\nimport kompile.testing.kotlinc\nimport org.intellij.lang.annotations.Language\ni"
  },
  {
    "path": "processor/src/test/java/permissions/dispatcher/processor/ProcessorTestSuite.java",
    "chars": 15368,
    "preview": "package permissions.dispatcher.processor;\n\nimport org.junit.Test;\nimport permissions.dispatcher.processor.base.TestSuite"
  },
  {
    "path": "processor/src/test/java/permissions/dispatcher/processor/base/AndroidAwareClassLoader.java",
    "chars": 1678,
    "preview": "package permissions.dispatcher.processor.base;\n\nimport org.apache.commons.io.IOUtils;\n\nimport java.io.IOException;\nimpor"
  },
  {
    "path": "processor/src/test/java/permissions/dispatcher/processor/base/BaseTest.java",
    "chars": 581,
    "preview": "package permissions.dispatcher.processor.base;\n\nimport com.google.testing.compile.JavaFileObjects;\n\nimport javax.tools.J"
  },
  {
    "path": "processor/src/test/java/permissions/dispatcher/processor/base/StringEquals.java",
    "chars": 583,
    "preview": "package permissions.dispatcher.processor.base;\n\nimport org.hamcrest.core.SubstringMatcher;\n\nfinal class StringEquals ext"
  },
  {
    "path": "processor/src/test/java/permissions/dispatcher/processor/base/TestSuite.java",
    "chars": 1204,
    "preview": "package permissions.dispatcher.processor.base;\n\nimport org.junit.Rule;\nimport org.junit.rules.ExpectedException;\n\nimport"
  },
  {
    "path": "processor/src/test/java/permissions/dispatcher/processor/data/Source.java",
    "chars": 302574,
    "preview": "package permissions.dispatcher.processor.data;\n\nimport permissions.dispatcher.processor.base.BaseTest;\n\npublic final cla"
  },
  {
    "path": "processor/src/test/java/permissions/dispatcher/processor/util/ExtensionsTest.kt",
    "chars": 4661,
    "preview": "package permissions.dispatcher.processor.util\n\nimport com.squareup.kotlinpoet.ClassName\nimport com.squareup.kotlinpoet.P"
  },
  {
    "path": "processor/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "chars": 17,
    "preview": "mock-maker-inline"
  },
  {
    "path": "sample/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "sample/build.gradle",
    "chars": 852,
    "preview": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-kapt'\n\nandroid {\n    compil"
  },
  {
    "path": "sample/src/main/AndroidManifest.xml",
    "chars": 1003,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "sample/src/main/kotlin/permissions/dispatcher/sample/MainActivity.kt",
    "chars": 4566,
    "preview": "package permissions.dispatcher.sample\n\nimport android.Manifest\nimport android.os.Bundle\nimport android.widget.Button\nimp"
  },
  {
    "path": "sample/src/main/kotlin/permissions/dispatcher/sample/camera/CameraPreview.kt",
    "chars": 4242,
    "preview": "package permissions.dispatcher.sample.camera\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimpo"
  },
  {
    "path": "sample/src/main/kotlin/permissions/dispatcher/sample/camera/CameraPreviewFragment.kt",
    "chars": 3619,
    "preview": "package permissions.dispatcher.sample.camera\n\nimport android.annotation.SuppressLint\nimport android.hardware.Camera\nimpo"
  },
  {
    "path": "sample/src/main/kotlin/permissions/dispatcher/sample/contacts/ContactsFragment.kt",
    "chars": 6027,
    "preview": "package permissions.dispatcher.sample.contacts\n\nimport android.content.ContentProviderOperation\nimport android.content.O"
  },
  {
    "path": "sample/src/main/res/layout/activity_main.xml",
    "chars": 1079,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/too"
  },
  {
    "path": "sample/src/main/res/layout/fragment_camera.xml",
    "chars": 604,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    and"
  },
  {
    "path": "sample/src/main/res/layout/fragment_contacts.xml",
    "chars": 1327,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    and"
  },
  {
    "path": "sample/src/main/res/values/dimens.xml",
    "chars": 280,
    "preview": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"margin_medium\">16dp"
  },
  {
    "path": "sample/src/main/res/values/strings.xml",
    "chars": 1736,
    "preview": "<resources>\n    <string name=\"app_name\">PermissionsDispatcher Sample</string>\n    <string name=\"back\">Back</string>\n    "
  },
  {
    "path": "sample/src/main/res/values/template-styles.xml",
    "chars": 100,
    "preview": "<resources>\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\" />\n</resources>\n"
  },
  {
    "path": "settings.gradle",
    "chars": 99,
    "preview": "include ':library', ':processor', ':lint', ':sample', ':test', ':annotation', ':ktx', ':ktx-sample'"
  },
  {
    "path": "test/.gitignore",
    "chars": 34,
    "preview": "/build\n!build/generated/source/apt"
  },
  {
    "path": "test/build.gradle",
    "chars": 1807,
    "preview": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-kapt'\n\nandroid {\n    compil"
  },
  {
    "path": "test/src/main/AndroidManifest.xml",
    "chars": 121,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"permissions.dispatcher.test\">\n\n    <application />\n\n</manifest"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityOnlyNeedsPermission.java",
    "chars": 761,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\n\nimport androidx.annotation.NonNull;\nimport androidx.appc"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithAllAnnotations.java",
    "chars": 1258,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\n\nimport androidx.annotation.NonNull;\nimport androidx.appc"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithAllAnnotationsKt.kt",
    "chars": 955,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport androidx.appcompat.app.AppCompatActivity\nimport perm"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithNoParameterArgumentKt.kt",
    "chars": 850,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport androidx.appcompat.app.AppCompatActivity\nimport perm"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithOnNeverAskAgain.java",
    "chars": 898,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\n\nimport androidx.annotation.NonNull;\nimport androidx.appc"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithOnPermissionDenied.java",
    "chars": 908,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\n\nimport androidx.annotation.NonNull;\nimport androidx.appc"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithParametersKt.kt",
    "chars": 687,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport androidx.appcompat.app.AppCompatActivity\nimport perm"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithShowRationale.java",
    "chars": 976,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\n\nimport androidx.annotation.NonNull;\nimport androidx.appc"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithSystemAlertWindow.java",
    "chars": 699,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\nimport android.content.Intent;\n\nimport androidx.appcompat"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithSystemAlertWindowAllAnnotations.java",
    "chars": 1136,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\nimport android.content.Intent;\n\nimport androidx.appcompat"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithSystemAlertWindowKt.kt",
    "chars": 626,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport android.content.Intent\nimport androidx.appcompat.app"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithSystemAlertWindowKtAllAnnotations.kt",
    "chars": 850,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport android.content.Intent\nimport androidx.appcompat.app"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithWriteSetting.java",
    "chars": 679,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\nimport android.content.Intent;\n\nimport androidx.appcompat"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithWriteSettingAllAnnotations.java",
    "chars": 1098,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\nimport android.content.Intent;\n\nimport androidx.appcompat"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithWriteSettingKt.kt",
    "chars": 610,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport android.content.Intent\nimport androidx.appcompat.app"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/ActivityWithWriteSettingKtAllAnnotations.kt",
    "chars": 817,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport android.content.Intent\nimport androidx.appcompat.app"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/FragmentWithAllAnnotations.java",
    "chars": 1239,
    "preview": "package permissions.dispatcher.test;\n\nimport android.Manifest;\n\nimport androidx.annotation.NonNull;\nimport androidx.frag"
  },
  {
    "path": "test/src/main/java/permissions/dispatcher/test/FragmentWithAllAnnotationsKt.kt",
    "chars": 853,
    "preview": "package permissions.dispatcher.test\n\nimport android.Manifest\nimport androidx.fragment.app.Fragment\nimport permissions.di"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityOnlyNeedsPermissionPermissionsDispatcherTest.kt",
    "chars": 3332,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.app.ActivityCompat\nim"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithAllAnnotationsKtPermissionsDispatcherTest.kt",
    "chars": 5346,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.app.ActivityCompat\nim"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithAllAnnotationsPermissionsDispatcherTest.kt",
    "chars": 5799,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.app.ActivityCompat\nim"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithNoParameterArgumentKtPermissionsDispatcherTest.kt",
    "chars": 5218,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.app.ActivityCompat\nim"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithOnNeverAskAgainPermissionsDispatcherTest.kt",
    "chars": 4130,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.app.ActivityCompat\nim"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithOnPermissionDeniedPermissionsDispatcherTest.kt",
    "chars": 4155,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.app.ActivityCompat\nim"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithParametersKtPermissionsDispatcherTest.kt",
    "chars": 1452,
    "preview": "package permissions.dispatcher.test\n\nimport androidx.core.app.ActivityCompat\nimport androidx.core.content.ContextCompat\n"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithShowRationalePermissionsDispatcherTest.kt",
    "chars": 4320,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.app.ActivityCompat\nim"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithSystemAlertWindowAllAnnotationsPermissionsDispatcherTest.kt",
    "chars": 4965,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithSystemAlertWindowKtAllAnnotationsTest.kt",
    "chars": 4668,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithSystemAlertWindowKtTest.kt",
    "chars": 3602,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithSystemAlertWindowPermissionsDispatcherTest.kt",
    "chars": 3927,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithWriteSettingAllAnnotationsPermissionsDispatcherTest.kt",
    "chars": 4144,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithWriteSettingKtAllAnnotationsTest.kt",
    "chars": 4542,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithWriteSettingKtTest.kt",
    "chars": 3510,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/ActivityWithWriteSettingPermissionsDispatcherTest.kt",
    "chars": 3786,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/Extensions.kt",
    "chars": 4562,
    "preview": "package permissions.dispatcher.test\n\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport android.c"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/FragmentWithAllAnnotationsKtPermissionsDispatcherTest.kt",
    "chars": 5316,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.content.ContextCompat"
  },
  {
    "path": "test/src/test/java/permissions/dispatcher/test/FragmentWithAllAnnotationsPermissionsDispatcherTest.kt",
    "chars": 5926,
    "preview": "package permissions.dispatcher.test\n\nimport android.content.pm.PackageManager\nimport androidx.core.content.ContextCompat"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the permissions-dispatcher/PermissionsDispatcher GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 185 files (745.7 KB), approximately 151.2k tokens, and a symbol index with 480 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!