Full Code of yanzhenjie/AndPermission for AI

master 00bedd6a3a93 cached
143 files
323.5 KB
74.3k tokens
600 symbols
1 requests
Download .txt
Showing preview only (365K chars total). Download the full file or copy to clipboard to get everything.
Repository: yanzhenjie/AndPermission
Branch: master
Commit: 00bedd6a3a93
Files: 143
Total size: 323.5 KB

Directory structure:
gitextract_hhydg_6p/

├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── build.gradle
├── config.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── permission/
│   ├── .gitignore
│   ├── build.gradle
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── aidl/
│           │   └── com/
│           │       └── yanzhenjie/
│           │           └── permission/
│           │               └── bridge/
│           │                   └── IBridge.aidl
│           ├── java/
│           │   └── com/
│           │       └── yanzhenjie/
│           │           └── permission/
│           │               ├── Action.java
│           │               ├── AndPermission.java
│           │               ├── Boot.java
│           │               ├── FileProvider.java
│           │               ├── Rationale.java
│           │               ├── RequestExecutor.java
│           │               ├── bridge/
│           │               │   ├── BridgeActivity.java
│           │               │   ├── BridgeRequest.java
│           │               │   ├── BridgeService.java
│           │               │   ├── Messenger.java
│           │               │   ├── RequestExecutor.java
│           │               │   └── RequestManager.java
│           │               ├── checker/
│           │               │   ├── CalendarReadTest.java
│           │               │   ├── CalendarWriteTest.java
│           │               │   ├── CallLogReadTest.java
│           │               │   ├── CallLogWriteTest.java
│           │               │   ├── CameraTest.java
│           │               │   ├── ContactsReadTest.java
│           │               │   ├── ContactsWriteTest.java
│           │               │   ├── DoubleChecker.java
│           │               │   ├── LocationCoarseTest.java
│           │               │   ├── LocationFineTest.java
│           │               │   ├── PermissionChecker.java
│           │               │   ├── PermissionTest.java
│           │               │   ├── PhoneStateReadTest.java
│           │               │   ├── RecordAudioTest.java
│           │               │   ├── SensorActivityTest.java
│           │               │   ├── SensorHeartTest.java
│           │               │   ├── SipTest.java
│           │               │   ├── SmsReadTest.java
│           │               │   ├── StandardChecker.java
│           │               │   ├── StorageReadTest.java
│           │               │   ├── StorageWriteTest.java
│           │               │   └── StrictChecker.java
│           │               ├── install/
│           │               │   ├── BaseRequest.java
│           │               │   ├── InstallRequest.java
│           │               │   ├── NRequest.java
│           │               │   ├── NRequestFactory.java
│           │               │   ├── ORequest.java
│           │               │   └── ORequestFactory.java
│           │               ├── notify/
│           │               │   ├── BaseRequest.java
│           │               │   ├── NRequest.java
│           │               │   ├── NRequestFactory.java
│           │               │   ├── Notify.java
│           │               │   ├── ORequest.java
│           │               │   ├── ORequestFactory.java
│           │               │   ├── PermissionRequest.java
│           │               │   ├── listener/
│           │               │   │   ├── BaseRequest.java
│           │               │   │   ├── J1Request.java
│           │               │   │   ├── J1RequestFactory.java
│           │               │   │   ├── J2Request.java
│           │               │   │   ├── J2RequestFactory.java
│           │               │   │   └── ListenerRequest.java
│           │               │   └── option/
│           │               │       └── NotifyOption.java
│           │               ├── option/
│           │               │   └── Option.java
│           │               ├── overlay/
│           │               │   ├── BaseRequest.java
│           │               │   ├── LRequest.java
│           │               │   ├── LRequestFactory.java
│           │               │   ├── MRequest.java
│           │               │   ├── MRequestFactory.java
│           │               │   ├── OverlayRequest.java
│           │               │   └── setting/
│           │               │       ├── LSettingPage.java
│           │               │       └── MSettingPage.java
│           │               ├── runtime/
│           │               │   ├── BaseRequest.java
│           │               │   ├── LRequest.java
│           │               │   ├── LRequestFactory.java
│           │               │   ├── MRequest.java
│           │               │   ├── MRequestFactory.java
│           │               │   ├── Permission.java
│           │               │   ├── PermissionDef.java
│           │               │   ├── PermissionRequest.java
│           │               │   ├── Runtime.java
│           │               │   ├── option/
│           │               │   │   └── RuntimeOption.java
│           │               │   └── setting/
│           │               │       ├── AllRequest.java
│           │               │       ├── SettingPage.java
│           │               │       └── SettingRequest.java
│           │               ├── setting/
│           │               │   ├── Setting.java
│           │               │   └── write/
│           │               │       ├── BaseRequest.java
│           │               │       ├── LWriteRequest.java
│           │               │       ├── LWriteRequestFactory.java
│           │               │       ├── MWriteRequest.java
│           │               │       ├── MWriteRequestFactory.java
│           │               │       └── WriteRequest.java
│           │               ├── source/
│           │               │   ├── ActivitySource.java
│           │               │   ├── ContextSource.java
│           │               │   ├── FragmentSource.java
│           │               │   ├── Source.java
│           │               │   ├── WrapperSource.java
│           │               │   └── XFragmentSource.java
│           │               ├── task/
│           │               │   ├── TaskExecutor.java
│           │               │   └── WaitDialog.java
│           │               └── util/
│           │                   └── StringUtils.java
│           └── res/
│               ├── drawable/
│               │   └── permission_shape_wait_background.xml
│               ├── layout/
│               │   └── permission_dialog_wait.xml
│               ├── values/
│               │   ├── dimens.xml
│               │   ├── string.xml
│               │   └── style.xml
│               ├── values-v21/
│               │   └── style.xml
│               ├── values-zh/
│               │   └── string.xml
│               ├── values-zh-RHK/
│               │   └── string.xml
│               ├── values-zh-RTW/
│               │   └── string.xml
│               └── xml/
│                   └── permission_file_paths.xml
├── sample/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── assets/
│           │   └── android.apk
│           ├── java/
│           │   └── com/
│           │       └── yanzhenjie/
│           │           └── permission/
│           │               └── sample/
│           │                   ├── App.java
│           │                   ├── InstallRationale.java
│           │                   ├── NotifyListenerRationale.java
│           │                   ├── NotifyRationale.java
│           │                   ├── OverlayRationale.java
│           │                   ├── RuntimeRationale.java
│           │                   ├── WriteSettingRationale.java
│           │                   ├── app/
│           │                   │   ├── MainActivity.java
│           │                   │   └── NotifyListenerService.java
│           │                   ├── util/
│           │                   │   ├── FileUtils.java
│           │                   │   └── IOUtils.java
│           │                   └── widget/
│           │                       ├── AlertWindow.java
│           │                       └── LauncherView.java
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   └── window_launcher.xml
│               └── values/
│                   ├── array_string.xml
│                   ├── colors.xml
│                   ├── dimens.xml
│                   ├── strings.xml
│                   └── styles.xml
└── settings.gradle

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

================================================
FILE: .gitignore
================================================
*.iml
/.idea/
/build/
.gradle
/local.properties
.DS_Store
/captures
.externalNativeBuild

================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to AndPermission
First off, thanks for taking the time to contribute.  

The following is a set of guidelines for contributing to AndPermission. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.

1. All your actions in AndPermission should be in English, not in other languages.
2. Please keep AndPermission the existing code style, not according to your habits.
3. Just modify the code you are sure need to be optimized, not all the different code from your ideas.
4. Before launching a pull request, you should test your commit code adequately.
5. Please commit new code to the [dev](https://github.com/yanzhenjie/AndPermission/tree/dev) branch instead of the master branch.

================================================
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 2019 Zhenjie Yan

   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
================================================
# AndPermission
1. Request for runtime permissions.  
2. Share private files.  
3. Request to install unknown source apk.  
  `android.permission.REQUEST_INSTALL_PACKAGES`
4. Request to draw at the top of other apps.  
  `android.permission.SYSTEM_ALERT_WINDOW`
5. Request to show notifications.  
6. Request to access notifications.  
  `android.permission.BIND_NOTIFICATION_LISTENER_SERVICE`
7. Request to modify system setting.  
  `android.permission.WRITE_SETTINGS`

```java
AndPermission.with(this)
  .runtime()
  .permission(Permission.Group.STORAGE)
  .onGranted(permissions -> {
    // Storage permission are allowed.
  })
  .onDenied(permissions -> {
    // Storage permission are not allowed.
  })
  .start();
```

For documentation and additional information see [the website](https://yanzhenjie.com/AndPermission).

## Download
It only supports androidx, add dependencies in your gradle:

```groovy
implementation 'com.yanzhenjie:permission:2.0.3'
```

AndPermission requires at minimum Android 4.0(Api level 14) .

## Contributing
Before submitting pull requests, contributors must abide by the [agreement](CONTRIBUTING.md) .

## License
```text
Copyright 2019 Zhenjie Yan

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: build.gradle
================================================
apply from: "config.gradle"

buildscript {
    repositories {
        google()
        jcenter {url 'https://maven.aliyun.com/repository/jcenter'}
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.1'
        classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
    }
}

allprojects {
    repositories {
        google()
        jcenter {url 'https://maven.aliyun.com/repository/jcenter'}
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

================================================
FILE: config.gradle
================================================
ext {
    plugins = [application: 'com.android.application',
               library    : 'com.android.library',
               maven      : 'com.github.dcendents.android-maven',
               bintray    : 'com.jfrog.bintray']

    android = [applicationId    : "com.yanzhenjie.permission.sample",
               compileSdkVersion: 29,
               buildToolsVersion: "29.0.2",
               minSdkVersion    : 14,
               targetSdkVersion : 29,
               versionCode      : 108,
               versionName      : "2.0.3"]

    bintray = [version       : "2.0.3",

               siteUrl       : 'https://github.com/yanzhenjie/AndPermission',
               gitUrl        : 'https://github.com/yanzhenjie/AndPermission.git',

               group         : "com.yanzhenjie",

               packaging     : 'aar',
               name          : 'Permission',
               description   : 'Permission manager for Android',

               licenseName   : 'The Apache Software License, Version 2.0',
               licenseUrl    : 'http://www.apache.org/licenses/LICENSE-2.0.txt',

               developerId   : 'yanzhenjie',
               developerName : 'yanzhenjie',
               developerEmail: 'im.yanzhenjie@gmail.com',

               binrayLibrary : "permission",
               bintrayRepo   : "maven",
               bintrayUser   : 'yolanda',
               bintrayLicense: "Apache-2.0"]

    dependencies = [
        fragment: 'androidx.appcompat:appcompat:1.1.0'
    ]
}

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

================================================
FILE: gradle.properties
================================================
android.useAndroidX=true


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

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

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

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

# 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
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
esac

# 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

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" ] ; 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

# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
    JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"

exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"


================================================
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

@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=

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

@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 Windowz variants

if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_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=%*
goto execute

:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
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: permission/.gitignore
================================================
/build

================================================
FILE: permission/build.gradle
================================================
apply plugin: rootProject.ext.plugins.library

android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion

    defaultConfig {
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
    }

    resourcePrefix 'permission'
}

dependencies {
    api rootProject.ext.dependencies.fragment
}

================================================
FILE: permission/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>

<!--
    Copyright 2017 Zhenjie Yan

    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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yanzhenjie.permission">

    <permission
        android:name="${applicationId}.andpermission.bridge"
        android:permissionGroup="${applicationId}.andpermission"
        android:protectionLevel="signature" />

    <uses-permission android:name="${applicationId}.andpermission.bridge" />

    <application>
        <service
            android:name=".bridge.BridgeService"
            android:exported="false"
            android:permission="${applicationId}.andpermission.bridge"
            android:process=":permission">
            <intent-filter>
                <action android:name="${applicationId}.andpermission.bridge" />
            </intent-filter>
        </service>

        <activity
            android:name=".bridge.BridgeActivity"
            android:configChanges="orientation"
            android:exported="false"
            android:permission="${applicationId}.andpermission.bridge"
            android:process=":permission"
            android:theme="@style/Permission.Theme.Activity.Transparent" />

        <provider
            android:name=".FileProvider"
            android:authorities="${applicationId}.file.path.share"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/permission_file_paths" />
        </provider>
    </application>

</manifest>

================================================
FILE: permission/src/main/aidl/com/yanzhenjie/permission/bridge/IBridge.aidl
================================================
package com.yanzhenjie.permission.bridge;

interface IBridge {
    /**
     * Request for permissions.
     */
    void requestAppDetails(in String suffix);

    /**
     * Request for permissions.
     */
    void requestPermission(in String suffix, in String[] permissions);

    /**
     * Request for package install.
     */
    void requestInstall(in String suffix);

   /**
    * Request for overlay.
    */
    void requestOverlay(in String suffix);

   /**
    * Request for alert window.
    */
    void requestAlertWindow(in String suffix);

   /**
    * Request for notify.
    */
    void requestNotify(in String suffix);

   /**
    * Request for notification listener.
    */
    void requestNotificationListener(in String suffix);

   /**
    * Request for write system setting.
    */
    void requestWriteSetting(in String suffix);
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/Action.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission;

/**
 * Created by Zhenjie Yan on 2018/1/1.
 */
public interface Action<T> {

    /**
     * An action.
     *
     * @param data the data.
     */
    void onAction(T data);
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/AndPermission.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;

import androidx.fragment.app.Fragment;

import com.yanzhenjie.permission.checker.DoubleChecker;
import com.yanzhenjie.permission.checker.PermissionChecker;
import com.yanzhenjie.permission.option.Option;
import com.yanzhenjie.permission.source.ActivitySource;
import com.yanzhenjie.permission.source.ContextSource;
import com.yanzhenjie.permission.source.FragmentSource;
import com.yanzhenjie.permission.source.Source;
import com.yanzhenjie.permission.source.XFragmentSource;

import java.io.File;
import java.util.List;

/**
 * Created by Zhenjie Yan on 2016/9/9.
 */
public class AndPermission {

    private static final String ACTION_BRIDGE_SUFFIX = ".andpermission.bridge";

    public static String bridgeAction(Context context, String suffix) {
        return context.getPackageName() + ACTION_BRIDGE_SUFFIX + (TextUtils.isEmpty(suffix) ? "" : "." + suffix);
    }

    /**
     * With context.
     *
     * @param context {@link Context}.
     * @return {@link Option}.
     */
    public static Option with(Context context) {
        return new Boot(getContextSource(context));
    }

    /**
     * With {@link Fragment}.
     *
     * @param fragment {@link Fragment}.
     * @return {@link Option}.
     */
    public static Option with(Fragment fragment) {
        return new Boot(new XFragmentSource(fragment));
    }

    /**
     * With {@link android.app.Fragment}.
     *
     * @param fragment {@link android.app.Fragment}.
     * @return {@link Option}.
     */
    public static Option with(android.app.Fragment fragment) {
        return new Boot(new FragmentSource(fragment));
    }

    /**
     * With activity.
     *
     * @param activity {@link Activity}.
     * @return {@link Option}.
     */
    public static Option with(Activity activity) {
        return new Boot(new ActivitySource(activity));
    }

    /**
     * Some privileges permanently disabled, may need to set up in the execute.
     *
     * @param context           {@link Context}.
     * @param deniedPermissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasAlwaysDeniedPermission(Context context, List<String> deniedPermissions) {
        return hasAlwaysDeniedPermission(getContextSource(context), deniedPermissions);
    }

    /**
     * Some privileges permanently disabled, may need to set up in the execute.
     *
     * @param fragment          {@link Fragment}.
     * @param deniedPermissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasAlwaysDeniedPermission(Fragment fragment, List<String> deniedPermissions) {
        return hasAlwaysDeniedPermission(new XFragmentSource(fragment), deniedPermissions);
    }

    /**
     * Some privileges permanently disabled, may need to set up in the execute.
     *
     * @param fragment          {@link android.app.Fragment}.
     * @param deniedPermissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasAlwaysDeniedPermission(android.app.Fragment fragment, List<String> deniedPermissions) {
        return hasAlwaysDeniedPermission(new FragmentSource(fragment), deniedPermissions);
    }

    /**
     * Some privileges permanently disabled, may need to set up in the execute.
     *
     * @param activity          {@link Activity}.
     * @param deniedPermissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasAlwaysDeniedPermission(Activity activity, List<String> deniedPermissions) {
        return hasAlwaysDeniedPermission(new ActivitySource(activity), deniedPermissions);
    }

    /**
     * Has always been denied permission.
     */
    private static boolean hasAlwaysDeniedPermission(Source source, List<String> deniedPermissions) {
        for (String permission : deniedPermissions) {
            if (!source.isShowRationalePermission(permission)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Some privileges permanently disabled, may need to set up in the execute.
     *
     * @param context           {@link Context}.
     * @param deniedPermissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasAlwaysDeniedPermission(Context context, String... deniedPermissions) {
        return hasAlwaysDeniedPermission(getContextSource(context), deniedPermissions);
    }

    /**
     * Some privileges permanently disabled, may need to set up in the execute.
     *
     * @param fragment          {@link Fragment}.
     * @param deniedPermissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasAlwaysDeniedPermission(Fragment fragment, String... deniedPermissions) {
        return hasAlwaysDeniedPermission(new XFragmentSource(fragment), deniedPermissions);
    }

    /**
     * Some privileges permanently disabled, may need to set up in the execute.
     *
     * @param fragment          {@link android.app.Fragment}.
     * @param deniedPermissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasAlwaysDeniedPermission(android.app.Fragment fragment, String... deniedPermissions) {
        return hasAlwaysDeniedPermission(new FragmentSource(fragment), deniedPermissions);
    }

    /**
     * Some privileges permanently disabled, may need to set up in the execute.
     *
     * @param activity          {@link Activity}.
     * @param deniedPermissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasAlwaysDeniedPermission(Activity activity, String... deniedPermissions) {
        return hasAlwaysDeniedPermission(new ActivitySource(activity), deniedPermissions);
    }

    /**
     * Has always been denied permission.
     */
    private static boolean hasAlwaysDeniedPermission(Source source, String... deniedPermissions) {
        for (String permission : deniedPermissions) {
            if (!source.isShowRationalePermission(permission)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Classic permission checker.
     */
    private static final PermissionChecker PERMISSION_CHECKER = new DoubleChecker();

    /**
     * Judgment already has the target permission.
     *
     * @param context     {@link Context}.
     * @param permissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasPermissions(Context context, String... permissions) {
        return PERMISSION_CHECKER.hasPermission(context, permissions);
    }

    /**
     * Judgment already has the target permission.
     *
     * @param fragment    {@link Fragment}.
     * @param permissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasPermissions(Fragment fragment, String... permissions) {
        return hasPermissions(fragment.getActivity(), permissions);
    }

    /**
     * Judgment already has the target permission.
     *
     * @param fragment    {@link android.app.Fragment}.
     * @param permissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasPermissions(android.app.Fragment fragment, String... permissions) {
        return hasPermissions(fragment.getActivity(), permissions);
    }

    /**
     * Judgment already has the target permission.
     *
     * @param activity    {@link Activity}.
     * @param permissions one or more permissions.
     * @return true, other wise is false.
     */
    public static boolean hasPermissions(Activity activity, String... permissions) {
        return PERMISSION_CHECKER.hasPermission(activity, permissions);
    }

    /**
     * Judgment already has the target permission.
     *
     * @param context     {@link Context}.
     * @param permissions one or more permission groups.
     * @return true, other wise is false.
     */
    public static boolean hasPermissions(Context context, String[]... permissions) {
        for (String[] permission : permissions) {
            boolean hasPermission = PERMISSION_CHECKER.hasPermission(context, permission);
            if (!hasPermission) return false;
        }
        return true;
    }

    /**
     * Judgment already has the target permission.
     *
     * @param fragment    {@link Fragment}.
     * @param permissions one or more permission groups.
     * @return true, other wise is false.
     */
    public static boolean hasPermissions(Fragment fragment, String[]... permissions) {
        return hasPermissions(fragment.getActivity(), permissions);
    }

    /**
     * Judgment already has the target permission.
     *
     * @param fragment    {@link android.app.Fragment}.
     * @param permissions one or more permission groups.
     * @return true, other wise is false.
     */
    public static boolean hasPermissions(android.app.Fragment fragment, String[]... permissions) {
        return hasPermissions(fragment.getActivity(), permissions);
    }

    /**
     * Judgment already has the target permission.
     *
     * @param activity    {@link Activity}.
     * @param permissions one or more permission groups.
     * @return true, other wise is false.
     */
    public static boolean hasPermissions(Activity activity, String[]... permissions) {
        for (String[] permission : permissions) {
            boolean hasPermission = PERMISSION_CHECKER.hasPermission(activity, permission);
            if (!hasPermission) return false;
        }
        return true;
    }

    /**
     * Get compatible Android 7.0 and lower versions of Uri.
     *
     * @param context {@link Context}.
     * @param file    apk file.
     * @return uri.
     */
    public static Uri getFileUri(Context context, File file) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return FileProvider.getUriForFile(context, context.getPackageName() + ".file.path.share", file);
        }
        return Uri.fromFile(file);
    }

    /**
     * Get compatible Android 7.0 and lower versions of Uri.
     *
     * @param fragment {@link Fragment}.
     * @param file     apk file.
     * @return uri.
     */
    public static Uri getFileUri(Fragment fragment, File file) {
        return getFileUri(fragment.getContext(), file);
    }

    /**
     * Get compatible Android 7.0 and lower versions of Uri.
     *
     * @param fragment {@link android.app.Fragment}.
     * @param file     apk file.
     * @return uri.
     */
    public static Uri getFileUri(android.app.Fragment fragment, File file) {
        return getFileUri(fragment.getActivity(), file);
    }

    private static Source getContextSource(Context context) {
        if (context instanceof Activity) {
            return new ActivitySource((Activity) context);
        } else if (context instanceof ContextWrapper) {
            return getContextSource(((ContextWrapper) context).getBaseContext());
        }
        return new ContextSource(context);
    }

    private AndPermission() {
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/Boot.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission;

import android.os.Build;

import com.yanzhenjie.permission.install.InstallRequest;
import com.yanzhenjie.permission.install.NRequestFactory;
import com.yanzhenjie.permission.install.ORequestFactory;
import com.yanzhenjie.permission.notify.Notify;
import com.yanzhenjie.permission.notify.option.NotifyOption;
import com.yanzhenjie.permission.option.Option;
import com.yanzhenjie.permission.overlay.LRequestFactory;
import com.yanzhenjie.permission.overlay.MRequestFactory;
import com.yanzhenjie.permission.overlay.OverlayRequest;
import com.yanzhenjie.permission.runtime.Runtime;
import com.yanzhenjie.permission.runtime.option.RuntimeOption;
import com.yanzhenjie.permission.setting.Setting;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/4/28.
 */
public class Boot implements Option {

    private static final InstallRequestFactory INSTALL_REQUEST_FACTORY;
    private static final OverlayRequestFactory OVERLAY_REQUEST_FACTORY;

    static {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            INSTALL_REQUEST_FACTORY = new ORequestFactory();
        } else {
            INSTALL_REQUEST_FACTORY = new NRequestFactory();
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            OVERLAY_REQUEST_FACTORY = new MRequestFactory();
        } else {
            OVERLAY_REQUEST_FACTORY = new LRequestFactory();
        }
    }

    public interface InstallRequestFactory {

        /**
         * Create apk installer request.
         */
        InstallRequest create(Source source);
    }

    public interface OverlayRequestFactory {

        /**
         * Create overlay request.
         */
        OverlayRequest create(Source source);
    }

    private Source mSource;

    public Boot(Source source) {
        this.mSource = source;
    }

    @Override
    public RuntimeOption runtime() {
        return new Runtime(mSource);
    }

    @Override
    public InstallRequest install() {
        return INSTALL_REQUEST_FACTORY.create(mSource);
    }

    @Override
    public OverlayRequest overlay() {
        return OVERLAY_REQUEST_FACTORY.create(mSource);
    }

    @Override
    public NotifyOption notification() {
        return new Notify(mSource);
    }

    @Override
    public Setting setting() {
        return new Setting(mSource);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/FileProvider.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.webkit.MimeTypeMap;

import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;

/**
 * <p>Copied from the support library v27.1.1.</p>
 * Created by Zhenjie Yan on 2018/4/28.
 */
public class FileProvider extends ContentProvider {

    private static final String[] COLUMNS = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};

    private static final String META_DATA_FILE_PROVIDER_PATHS = "android.support.FILE_PROVIDER_PATHS";

    private static final String TAG_ROOT_PATH = "root-path";
    private static final String TAG_FILES_PATH = "files-path";
    private static final String TAG_CACHE_PATH = "cache-path";
    private static final String TAG_EXTERNAL = "external-path";
    private static final String TAG_EXTERNAL_FILES = "external-files-path";
    private static final String TAG_EXTERNAL_CACHE = "external-cache-path";
    private static final String TAG_EXTERNAL_MEDIA = "external-media-path";

    private static final String ATTR_NAME = "name";
    private static final String ATTR_PATH = "path";

    private static final File DEVICE_ROOT = new File("/");

    private static final HashMap<String, PathStrategy> sCache = new HashMap<>();

    private PathStrategy mStrategy;

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public void attachInfo(Context context, ProviderInfo info) {
        super.attachInfo(context, info);
        if (info.exported) {
            throw new SecurityException("Provider must not be exported");
        }
        if (!info.grantUriPermissions) {
            throw new SecurityException("Provider must grant uri permissions");
        }

        mStrategy = getPathStrategy(context, info.authority);
    }

    public static Uri getUriForFile(Context context, String authority, File file) {
        final PathStrategy strategy = getPathStrategy(context, authority);
        return strategy.getUriForFile(file);
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        final File file = mStrategy.getFileForUri(uri);
        if (projection == null) {
            projection = COLUMNS;
        }

        String[] cols = new String[projection.length];
        Object[] values = new Object[projection.length];
        int i = 0;
        for (String col : projection) {
            if (OpenableColumns.DISPLAY_NAME.equals(col)) {
                cols[i] = OpenableColumns.DISPLAY_NAME;
                values[i++] = file.getName();
            } else if (OpenableColumns.SIZE.equals(col)) {
                cols[i] = OpenableColumns.SIZE;
                values[i++] = file.length();
            }
        }

        cols = copyOf(cols, i);
        values = copyOf(values, i);

        final MatrixCursor cursor = new MatrixCursor(cols, 1);
        cursor.addRow(values);
        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        final File file = mStrategy.getFileForUri(uri);

        final int lastDot = file.getName().lastIndexOf('.');
        if (lastDot >= 0) {
            final String extension = file.getName().substring(lastDot + 1);
            final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
            if (mime != null) {
                return mime;
            }
        }
        return "application/octet-stream";
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        throw new UnsupportedOperationException("No external inserts");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        throw new UnsupportedOperationException("No external updates");
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        final File file = mStrategy.getFileForUri(uri);
        return file.delete() ? 1 : 0;
    }

    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
        final File file = mStrategy.getFileForUri(uri);
        final int fileMode = modeToMode(mode);
        return ParcelFileDescriptor.open(file, fileMode);
    }

    private static PathStrategy getPathStrategy(Context context, String authority) {
        PathStrategy strategy;
        synchronized (sCache) {
            strategy = sCache.get(authority);
            if (strategy == null) {
                try {
                    strategy = parsePathStrategy(context, authority);
                } catch (IOException e) {
                    throw new IllegalArgumentException(
                        "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
                } catch (XmlPullParserException e) {
                    throw new IllegalArgumentException(
                        "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
                }
                sCache.put(authority, strategy);
            }
        }
        return strategy;
    }

    private static PathStrategy parsePathStrategy(Context context, String authority)
        throws IOException, XmlPullParserException {
        final SimplePathStrategy strategy = new SimplePathStrategy(authority);
        final ProviderInfo info = context.getPackageManager()
            .resolveContentProvider(authority, PackageManager.GET_META_DATA);
        final XmlResourceParser in = info.loadXmlMetaData(context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
        if (in == null) {
            throw new IllegalArgumentException("Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");
        }

        int type;
        while ((type = in.next()) != END_DOCUMENT) {
            if (type == START_TAG) {
                final String tag = in.getName();

                final String name = in.getAttributeValue(null, ATTR_NAME);
                String path = in.getAttributeValue(null, ATTR_PATH);

                File target = null;
                if (TAG_ROOT_PATH.equals(tag)) {
                    target = DEVICE_ROOT;
                } else if (TAG_FILES_PATH.equals(tag)) {
                    target = context.getFilesDir();
                } else if (TAG_CACHE_PATH.equals(tag)) {
                    target = context.getCacheDir();
                } else if (TAG_EXTERNAL.equals(tag)) {
                    target = Environment.getExternalStorageDirectory();
                } else if (TAG_EXTERNAL_FILES.equals(tag)) {
                    File[] externalFilesDirs = getExternalFilesDirs(context, null);
                    if (externalFilesDirs.length > 0) {
                        target = externalFilesDirs[0];
                    }
                } else if (TAG_EXTERNAL_CACHE.equals(tag)) {
                    File[] externalCacheDirs = getExternalCacheDirs(context);
                    if (externalCacheDirs.length > 0) {
                        target = externalCacheDirs[0];
                    }
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && TAG_EXTERNAL_MEDIA.equals(tag)) {
                    File[] externalMediaDirs = context.getExternalMediaDirs();
                    if (externalMediaDirs.length > 0) {
                        target = externalMediaDirs[0];
                    }
                }

                if (target != null) {
                    strategy.addRoot(name, buildPath(target, path));
                }
            }
        }

        return strategy;
    }

    interface PathStrategy {

        Uri getUriForFile(File file);

        File getFileForUri(Uri uri);
    }

    static class SimplePathStrategy implements PathStrategy {

        private final String mAuthority;
        private final HashMap<String, File> mRoots = new HashMap<String, File>();

        SimplePathStrategy(String authority) {
            mAuthority = authority;
        }

        void addRoot(String name, File root) {
            if (TextUtils.isEmpty(name)) {
                throw new IllegalArgumentException("Name must not be empty");
            }

            try {
                root = root.getCanonicalFile();
            } catch (IOException e) {
                throw new IllegalArgumentException("Failed to resolve canonical path for " + root, e);
            }

            mRoots.put(name, root);
        }

        @Override
        public Uri getUriForFile(File file) {
            String path;
            try {
                path = file.getCanonicalPath();
            } catch (IOException e) {
                throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
            }

            Map.Entry<String, File> mostSpecific = null;
            for (Map.Entry<String, File> root : mRoots.entrySet()) {
                final String rootPath = root.getValue().getPath();
                boolean invalidMost = mostSpecific == null ||
                    rootPath.length() > mostSpecific.getValue().getPath().length();
                if (path.startsWith(rootPath) && invalidMost) {
                    mostSpecific = root;
                }
            }

            if (mostSpecific == null) {
                throw new IllegalArgumentException("Failed to find configured root that contains " + path);
            }

            final String rootPath = mostSpecific.getValue().getPath();
            if (rootPath.endsWith("/")) {
                path = path.substring(rootPath.length());
            } else {
                path = path.substring(rootPath.length() + 1);
            }

            path = Uri.encode(mostSpecific.getKey()) + '/' + Uri.encode(path, "/");
            return new Uri.Builder().scheme("content").authority(mAuthority).encodedPath(path).build();
        }

        @Override
        public File getFileForUri(Uri uri) {
            String path = uri.getEncodedPath();

            final int splitIndex = path.indexOf('/', 1);
            final String tag = Uri.decode(path.substring(1, splitIndex));
            path = Uri.decode(path.substring(splitIndex + 1));

            final File root = mRoots.get(tag);
            if (root == null) {
                throw new IllegalArgumentException("Unable to find configured root for " + uri);
            }

            File file = new File(root, path);
            try {
                file = file.getCanonicalFile();
            } catch (IOException e) {
                throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
            }

            if (!file.getPath().startsWith(root.getPath())) {
                throw new SecurityException("Resolved path jumped beyond configured root");
            }
            return file;
        }
    }

    private static int modeToMode(String mode) {
        int modeBits;
        if ("r".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
        } else if ("w".equals(mode) || "wt".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE |
                ParcelFileDescriptor.MODE_TRUNCATE;
        } else if ("wa".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE |
                ParcelFileDescriptor.MODE_APPEND;
        } else if ("rw".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE;
        } else if ("rwt".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE |
                ParcelFileDescriptor.MODE_TRUNCATE;
        } else {
            throw new IllegalArgumentException("Invalid mode: " + mode);
        }
        return modeBits;
    }

    private static File buildPath(File base, String... segments) {
        File cur = base;
        for (String segment : segments) {
            if (segment != null) {
                cur = new File(cur, segment);
            }
        }
        return cur;
    }

    private static String[] copyOf(String[] original, int newLength) {
        final String[] result = new String[newLength];
        System.arraycopy(original, 0, result, 0, newLength);
        return result;
    }

    private static Object[] copyOf(Object[] original, int newLength) {
        final Object[] result = new Object[newLength];
        System.arraycopy(original, 0, result, 0, newLength);
        return result;
    }

    private static File[] getExternalFilesDirs(Context context, String type) {
        if (Build.VERSION.SDK_INT >= 19) {
            return context.getExternalFilesDirs(type);
        } else {
            return new File[] {context.getExternalFilesDir(type)};
        }
    }

    public static File[] getExternalCacheDirs(Context context) {
        if (Build.VERSION.SDK_INT >= 19) {
            return context.getExternalCacheDirs();
        } else {
            return new File[] {context.getExternalCacheDir()};
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/Rationale.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission;

import android.content.Context;

/**
 * Created by Zhenjie Yan on 2016/9/10.
 */
public interface Rationale<T> {

    /**
     * Show rationale to user.
     *
     * @param context context.
     * @param data the data.
     * @param executor executor.
     */
    void showRationale(Context context, T data, RequestExecutor executor);
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/RequestExecutor.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission;

/**
 * <p>Request executor.</p>
 * Created by Zhenjie Yan on 2016/9/10.
 */
public interface RequestExecutor {

    /**
     * Go request permission.
     */
    void execute();

    /**
     * Cancel the operation.
     */
    void cancel();

}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeActivity.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.bridge;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.view.KeyEvent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.yanzhenjie.permission.overlay.setting.LSettingPage;
import com.yanzhenjie.permission.overlay.setting.MSettingPage;
import com.yanzhenjie.permission.source.ActivitySource;
import com.yanzhenjie.permission.source.Source;

/**
 * <p>
 * Request permission.
 * </p>
 * Created by Zhenjie Yan on 2017/4/27.
 */
public final class BridgeActivity extends Activity {

    private static final String KEY_TYPE = "KEY_TYPE";
    private static final String KEY_PERMISSIONS = "KEY_PERMISSIONS";
    private static final String KEY_ACTION_SUFFIX = "KEY_ACTION_SUFFIX";

    /**
     * Request for permissions.
     */
    static void requestAppDetails(Source source, String suffix) {
        Intent intent = new Intent(source.getContext(), BridgeActivity.class);
        intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_APP_DETAILS);
        intent.putExtra(KEY_ACTION_SUFFIX, suffix);
        source.startActivity(intent);
    }

    /**
     * Request for permissions.
     */
    static void requestPermission(Source source, String suffix, String[] permissions) {
        Intent intent = new Intent(source.getContext(), BridgeActivity.class);
        intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_PERMISSION);
        intent.putExtra(KEY_PERMISSIONS, permissions);
        intent.putExtra(KEY_ACTION_SUFFIX, suffix);
        source.startActivity(intent);
    }

    /**
     * Request for package install.
     */
    static void requestInstall(Source source, String suffix) {
        Intent intent = new Intent(source.getContext(), BridgeActivity.class);
        intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_INSTALL);
        intent.putExtra(KEY_ACTION_SUFFIX, suffix);
        source.startActivity(intent);
    }

    /**
     * Request for overlay.
     */
    static void requestOverlay(Source source, String suffix) {
        Intent intent = new Intent(source.getContext(), BridgeActivity.class);
        intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_OVERLAY);
        intent.putExtra(KEY_ACTION_SUFFIX, suffix);
        source.startActivity(intent);
    }

    /**
     * Request for alert window.
     */
    static void requestAlertWindow(Source source, String suffix) {
        Intent intent = new Intent(source.getContext(), BridgeActivity.class);
        intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_ALERT_WINDOW);
        intent.putExtra(KEY_ACTION_SUFFIX, suffix);
        source.startActivity(intent);
    }

    /**
     * Request for notify.
     */
    static void requestNotify(Source source, String suffix) {
        Intent intent = new Intent(source.getContext(), BridgeActivity.class);
        intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_NOTIFY);
        intent.putExtra(KEY_ACTION_SUFFIX, suffix);
        source.startActivity(intent);
    }

    /**
     * Request for notification listener.
     */
    static void requestNotificationListener(Source source, String suffix) {
        Intent intent = new Intent(source.getContext(), BridgeActivity.class);
        intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_NOTIFY_LISTENER);
        intent.putExtra(KEY_ACTION_SUFFIX, suffix);
        source.startActivity(intent);
    }

    /**
     * Request for write system setting.
     */
    static void requestWriteSetting(Source source, String suffix) {
        Intent intent = new Intent(source.getContext(), BridgeActivity.class);
        intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_WRITE_SETTING);
        intent.putExtra(KEY_ACTION_SUFFIX, suffix);
        source.startActivity(intent);
    }

    private String mActionSuffix;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) return;

        Intent intent = getIntent();
        int operation = intent.getIntExtra(KEY_TYPE, -1);
        mActionSuffix = intent.getStringExtra(KEY_ACTION_SUFFIX);
        switch (operation) {
            case BridgeRequest.TYPE_APP_DETAILS: {
                Intent appDetailsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                appDetailsIntent.setData(Uri.fromParts("package", getPackageName(), null));
                startActivityForResult(appDetailsIntent, BridgeRequest.TYPE_APP_DETAILS);
                break;
            }
            case BridgeRequest.TYPE_PERMISSION: {
                String[] permissions = intent.getStringArrayExtra(KEY_PERMISSIONS);
                requestPermissions(permissions, BridgeRequest.TYPE_PERMISSION);
                break;
            }
            case BridgeRequest.TYPE_INSTALL: {
                Intent manageIntent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
                manageIntent.setData(Uri.fromParts("package", getPackageName(), null));
                startActivityForResult(manageIntent, BridgeRequest.TYPE_INSTALL);
                break;
            }
            case BridgeRequest.TYPE_OVERLAY: {
                MSettingPage settingPage = new MSettingPage(new ActivitySource(this));
                settingPage.start(BridgeRequest.TYPE_OVERLAY);
                break;
            }
            case BridgeRequest.TYPE_ALERT_WINDOW: {
                LSettingPage settingPage = new LSettingPage(new ActivitySource(this));
                settingPage.start(BridgeRequest.TYPE_ALERT_WINDOW);
                break;
            }
            case BridgeRequest.TYPE_NOTIFY: {
                Intent settingIntent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
                settingIntent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
                settingIntent.setData(Uri.fromParts("package", getPackageName(), null));
                startActivityForResult(settingIntent, BridgeRequest.TYPE_NOTIFY);
                break;
            }
            case BridgeRequest.TYPE_NOTIFY_LISTENER: {
                Intent settingIntent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
                settingIntent.setData(Uri.fromParts("package", getPackageName(), null));
                startActivityForResult(settingIntent, BridgeRequest.TYPE_NOTIFY_LISTENER);
                break;
            }
            case BridgeRequest.TYPE_WRITE_SETTING: {
                Intent settingIntent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
                settingIntent.setData(Uri.fromParts("package", getPackageName(), null));
                startActivityForResult(settingIntent, BridgeRequest.TYPE_WRITE_SETTING);
                break;
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Messenger.send(this, mActionSuffix);
        finish();
    }

    @Override
    public void startActivityForResult(Intent intent, int requestCode) {
        super.startActivityForResult(intent, requestCode);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Messenger.send(this, mActionSuffix);
        finish();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeRequest.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.bridge;

import com.yanzhenjie.permission.source.Source;

import java.util.List;

/**
 * Created by Zhenjie Yan on 2/13/19.
 */
public final class BridgeRequest {

    public static final int TYPE_APP_DETAILS = 1;
    public static final int TYPE_PERMISSION = 2;
    public static final int TYPE_INSTALL = 3;
    public static final int TYPE_OVERLAY = 4;
    public static final int TYPE_ALERT_WINDOW = 5;
    public static final int TYPE_NOTIFY = 6;
    public static final int TYPE_NOTIFY_LISTENER = 7;
    public static final int TYPE_WRITE_SETTING = 8;

    private final Source mSource;

    private int mType;
    private Callback mCallback;
    private List<String> mPermissions;

    public BridgeRequest(Source source) {
        this.mSource = source;
    }

    public Source getSource() {
        return mSource;
    }

    public int getType() {
        return mType;
    }

    public void setType(int type) {
        mType = type;
    }

    public Callback getCallback() {
        return mCallback;
    }

    public void setCallback(Callback callback) {
        mCallback = callback;
    }

    public List<String> getPermissions() {
        return mPermissions;
    }

    public void setPermissions(List<String> permissions) {
        mPermissions = permissions;
    }

    public interface Callback {

        void onCallback();
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeService.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.bridge;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

import androidx.annotation.Nullable;

import com.yanzhenjie.permission.source.ContextSource;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2019-08-30.
 */
public class BridgeService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mStub.asBinder();
    }

    private IBridge.Stub mStub = new IBridge.Stub() {

        private Source mSource = new ContextSource(BridgeService.this);

        @Override
        public void requestAppDetails(String suffix) throws RemoteException {
            BridgeActivity.requestAppDetails(mSource, suffix);
        }

        @Override
        public void requestPermission(String suffix, String[] permissions) throws RemoteException {
            BridgeActivity.requestPermission(mSource, suffix, permissions);
        }

        @Override
        public void requestInstall(String suffix) throws RemoteException {
            BridgeActivity.requestInstall(mSource, suffix);
        }

        @Override
        public void requestOverlay(String suffix) throws RemoteException {
            BridgeActivity.requestOverlay(mSource, suffix);
        }

        @Override
        public void requestAlertWindow(String suffix) throws RemoteException {
            BridgeActivity.requestAlertWindow(mSource, suffix);
        }

        @Override
        public void requestNotify(String suffix) throws RemoteException {
            BridgeActivity.requestNotify(mSource, suffix);
        }

        @Override
        public void requestNotificationListener(String suffix) throws RemoteException {
            BridgeActivity.requestNotificationListener(mSource, suffix);
        }

        @Override
        public void requestWriteSetting(String suffix) throws RemoteException {
            BridgeActivity.requestWriteSetting(mSource, suffix);
        }
    };
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/Messenger.java
================================================
/*
 * Copyright © 2018 Zhenjie Yan.
 *
 * 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.
 */
package com.yanzhenjie.permission.bridge;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import com.yanzhenjie.permission.AndPermission;

/**
 * Created by Zhenjie Yan on 2018/6/9.
 */
class Messenger extends BroadcastReceiver {

    public static void send(Context context, String suffix) {
        Intent broadcast = new Intent(AndPermission.bridgeAction(context, suffix));
        context.sendBroadcast(broadcast);
    }

    private final Context mContext;
    private final Callback mCallback;

    public Messenger(Context context, Callback callback) {
        this.mContext = context;
        this.mCallback = callback;
    }

    public void register(String suffix) {
        IntentFilter filter = new IntentFilter(AndPermission.bridgeAction(mContext, suffix));
        mContext.registerReceiver(this, filter);
    }

    public void unRegister() {
        mContext.unregisterReceiver(this);
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        mCallback.onCallback();
    }

    public interface Callback {

        void onCallback();
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/RequestExecutor.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.bridge;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;

import com.yanzhenjie.permission.AndPermission;

import java.util.List;

/**
 * Created by Zhenjie Yan on 2/13/19.
 */
final class RequestExecutor extends Thread implements Messenger.Callback {

    private BridgeRequest mRequest;
    private Messenger mMessenger;

    public RequestExecutor(BridgeRequest request) {
        this.mRequest = request;
    }

    @Override
    public void run() {
        Context context = mRequest.getSource().getContext();

        mMessenger = new Messenger(context, this);
        mMessenger.register(getName());

        Intent intent = new Intent();
        intent.setAction(AndPermission.bridgeAction(context, null));
        intent.setPackage(context.getPackageName());
        context.bindService(intent, mConnection, Service.BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBridge iBridge = IBridge.Stub.asInterface(iBinder);
            try {
                executeCurrent(iBridge);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    };

    private void executeCurrent(IBridge iBridge) throws RemoteException {
        switch (mRequest.getType()) {
            case BridgeRequest.TYPE_APP_DETAILS: {
                iBridge.requestAppDetails(getName());
                break;
            }
            case BridgeRequest.TYPE_PERMISSION: {
                List<String> permissions = mRequest.getPermissions();
                String[] array = permissions.toArray(new String[0]);
                iBridge.requestPermission(getName(), array);
                break;
            }
            case BridgeRequest.TYPE_INSTALL: {
                iBridge.requestInstall(getName());
                break;
            }
            case BridgeRequest.TYPE_OVERLAY: {
                iBridge.requestOverlay(getName());
                break;
            }
            case BridgeRequest.TYPE_ALERT_WINDOW: {
                iBridge.requestAlertWindow(getName());
                break;
            }
            case BridgeRequest.TYPE_NOTIFY: {
                iBridge.requestNotify(getName());
                break;
            }
            case BridgeRequest.TYPE_NOTIFY_LISTENER: {
                iBridge.requestNotificationListener(getName());
                break;
            }
            case BridgeRequest.TYPE_WRITE_SETTING: {
                iBridge.requestWriteSetting(getName());
                break;
            }
        }
    }


    @Override
    public void onCallback() {
        synchronized (this) {
            mMessenger.unRegister();
            mRequest.getCallback().onCallback();
            mRequest.getSource().getContext().unbindService(mConnection);
            mMessenger = null;
            mRequest = null;
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/RequestManager.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.bridge;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Created by Zhenjie Yan on 2/13/19.
 */
public class RequestManager {

    private static RequestManager sManager;

    public static RequestManager get() {
        if (sManager == null) {
            synchronized (RequestManager.class) {
                if (sManager == null) {
                    sManager = new RequestManager();
                }
            }
        }
        return sManager;
    }

    private final Executor mExecutor;

    private RequestManager() {
        this.mExecutor = Executors.newCachedThreadPool();
    }

    public void add(BridgeRequest request) {
        mExecutor.execute(new RequestExecutor(request));
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CalendarReadTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.CalendarContract;

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class CalendarReadTest implements PermissionTest {

    private ContentResolver mResolver;

    CalendarReadTest(Context context) {
        mResolver = context.getContentResolver();
    }

    @Override
    public boolean test() throws Throwable {
        String[] projection = new String[] {CalendarContract.Calendars._ID, CalendarContract.Calendars.NAME};
        Cursor cursor = mResolver.query(CalendarContract.Calendars.CONTENT_URI, projection, null, null, null);
        if (cursor != null) {
            try {
                CursorTest.read(cursor);
            } finally {
                cursor.close();
            }
            return true;
        } else {
            return false;
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CalendarWriteTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.graphics.Color;
import android.net.Uri;
import android.provider.CalendarContract;

import com.yanzhenjie.permission.util.StringUtils;

import java.util.TimeZone;

/**
 * Created by Zhenjie Yan on 2018/1/15.
 */
class CalendarWriteTest implements PermissionTest {

    private static final String NAME = StringUtils.hexToText("5045524D495353494F4E");
    private static final String ACCOUNT = StringUtils.hexToText("7065726D697373696F6E40676D61696C2E636F6D");

    private ContentResolver mResolver;

    CalendarWriteTest(Context context) {
        this.mResolver = context.getContentResolver();
    }

    @Override
    public boolean test() throws Throwable {
        try {
            TimeZone timeZone = TimeZone.getDefault();
            ContentValues value = new ContentValues();
            value.put(CalendarContract.Calendars.NAME, NAME);
            value.put(CalendarContract.Calendars.ACCOUNT_NAME, ACCOUNT);
            value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL);
            value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, NAME);
            value.put(CalendarContract.Calendars.VISIBLE, 1);
            value.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.BLUE);
            value.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER);
            value.put(CalendarContract.Calendars.SYNC_EVENTS, 1);
            value.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timeZone.getID());
            value.put(CalendarContract.Calendars.OWNER_ACCOUNT, NAME);
            value.put(CalendarContract.Calendars.CAN_ORGANIZER_RESPOND, 0);

            Uri insertUri = CalendarContract.Calendars.CONTENT_URI.buildUpon()
                .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
                .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, NAME)
                .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL)
                .build();
            Uri resourceUri = mResolver.insert(insertUri, value);
            return ContentUris.parseId(resourceUri) > 0;
        } finally {
            Uri deleteUri = CalendarContract.Calendars.CONTENT_URI.buildUpon().build();
            mResolver.delete(deleteUri, CalendarContract.Calendars.ACCOUNT_NAME + "=?", new String[]{ACCOUNT});
        }
    }
}


================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CallLogReadTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.CallLog;

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class CallLogReadTest implements PermissionTest {

    private ContentResolver mResolver;

    CallLogReadTest(Context context) {
        mResolver = context.getContentResolver();
    }

    @Override
    public boolean test() throws Throwable {
        String[] projection = new String[] {CallLog.Calls._ID, CallLog.Calls.NUMBER, CallLog.Calls.TYPE};
        Cursor cursor = mResolver.query(CallLog.Calls.CONTENT_URI, projection, null, null, null);
        if (cursor != null) {
            try {
                CursorTest.read(cursor);
            } finally {
                cursor.close();
            }
            return true;
        } else {
            return false;
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CallLogWriteTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.provider.CallLog;

/**
 * Created by Zhenjie Yan on 2018/1/14.
 */
class CallLogWriteTest implements PermissionTest {

    private ContentResolver mResolver;

    CallLogWriteTest(Context context) {
        this.mResolver = context.getContentResolver();
    }

    @Override
    public boolean test() throws Throwable {
        try {
            ContentValues content = new ContentValues();
            content.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE);
            content.put(CallLog.Calls.NUMBER, "1");
            content.put(CallLog.Calls.DATE, 20080808);
            content.put(CallLog.Calls.NEW, "0");
            Uri resourceUri = mResolver.insert(CallLog.Calls.CONTENT_URI, content);
            return ContentUris.parseId(resourceUri) > 0;
        } finally {
            mResolver.delete(CallLog.Calls.CONTENT_URI, CallLog.Calls.NUMBER + "=?", new String[] {"1"});
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CameraTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Camera;

/**
 * Created by Zhenjie Yan on 2018/1/15.
 */
class CameraTest implements PermissionTest {

    private Context mContext;

    CameraTest(Context context) {
        this.mContext = context;
    }

    @Override
    public boolean test() throws Throwable {
        Camera camera = null;
        try {
            int cameraCount = Camera.getNumberOfCameras();
            if (cameraCount <= 0) return true;

            camera = Camera.open(cameraCount - 1);
            Camera.Parameters parameters = camera.getParameters();
            camera.setParameters(parameters);
            camera.setPreviewCallback(PREVIEW_CALLBACK);
            camera.startPreview();
            return true;
        } catch (Throwable e) {
            PackageManager packageManager = mContext.getPackageManager();
            return !packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
        } finally {
            if (camera != null) {
                camera.stopPreview();
                camera.setPreviewCallback(null);
                camera.release();
            }
        }
    }

    private static final Camera.PreviewCallback PREVIEW_CALLBACK = new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
        }
    };
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/ContactsReadTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class ContactsReadTest implements PermissionTest {

    private ContentResolver mResolver;

    ContactsReadTest(Context context) {
        mResolver = context.getContentResolver();
    }

    @Override
    public boolean test() throws Throwable {
        String[] projection = new String[] {ContactsContract.Data._ID, ContactsContract.Data.DATA1};
        Cursor cursor = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null,
            null);
        if (cursor != null) {
            try {
                CursorTest.read(cursor);
            } finally {
                cursor.close();
            }
            return true;
        } else {
            return false;
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/ContactsWriteTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;

import androidx.annotation.Nullable;

/**
 * Created by Zhenjie Yan on 2018/1/14.
 */
class ContactsWriteTest implements PermissionTest {

    private static final String DISPLAY_NAME = "PERMISSION";

    private ContentResolver mResolver;

    ContactsWriteTest(ContentResolver resolver) {
        this.mResolver = resolver;
    }

    @Override
    public boolean test() throws Throwable {
        long[] idArray = insert();
        long rawContactId = idArray[0];
        long dataId = idArray[1];
        if (rawContactId > 0 && dataId > 0) {
            return delete(rawContactId, dataId);
        }
        return false;
    }

    private long[] insert() {
        ContentValues values = new ContentValues();
        Uri rawContractUri = mResolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
        long rawContactId = ContentUris.parseId(rawContractUri);

        values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
        values.put(ContactsContract.Data.DATA1, DISPLAY_NAME);
        values.put(ContactsContract.Data.DATA2, DISPLAY_NAME);
        values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
        Uri dataUri = mResolver.insert(ContactsContract.Data.CONTENT_URI, values);
        long dataId = ContentUris.parseId(dataUri);
        return new long[]{rawContactId, dataId};
    }

    private boolean delete(long rawContactId, long dataId) {
        int dataCount = mResolver.delete(ContactsContract.Data.CONTENT_URI, ContactsContract.Data._ID + "=?",
            new String[]{Long.toString(dataId)});
        int rawContactCount = mResolver.delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.RawContacts._ID + "=?",
            new String[]{Long.toString(rawContactId)});
        return rawContactCount > 0 && dataCount > 0;
    }

    private boolean update(long rawContactId) {
        ContentValues values = new ContentValues();
        values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
        values.put(ContactsContract.Data.DATA1, DISPLAY_NAME);
        values.put(ContactsContract.Data.DATA2, DISPLAY_NAME);
        values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
        Uri dataUri = mResolver.insert(ContactsContract.Data.CONTENT_URI, values);
        return ContentUris.parseId(dataUri) > 0;
    }

    @Nullable
    private long[] query() {
        Cursor cursor = mResolver.query(ContactsContract.Data.CONTENT_URI,
            new String[]{ContactsContract.Data.RAW_CONTACT_ID, ContactsContract.Data._ID},
            ContactsContract.Data.MIMETYPE + "=? and " + ContactsContract.Data.DATA1 + "=?",
            new String[]{ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, DISPLAY_NAME}, null);
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                long rawContactId = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID));
                long dataId = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data._ID));
                return new long[]{rawContactId, dataId};
            }
        }
        return null;
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/DoubleChecker.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;

import java.util.List;

/**
 * Created by Zhenjie Yan on 2018/1/27.
 */
public final class DoubleChecker implements PermissionChecker {

    private static final PermissionChecker STANDARD_CHECKER = new StandardChecker();
    private static final PermissionChecker STRICT_CHECKER = new StrictChecker();

    @Override
    public boolean hasPermission(Context context, String... permissions) {
        return STRICT_CHECKER.hasPermission(context, permissions) &&
            STANDARD_CHECKER.hasPermission(context, permissions);
    }

    @Override
    public boolean hasPermission(Context context, List<String> permissions) {
        return STRICT_CHECKER.hasPermission(context, permissions) &&
            STANDARD_CHECKER.hasPermission(context, permissions);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/LocationCoarseTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.content.pm.PackageManager;
import android.location.LocationManager;

import java.util.List;

/**
 * Created by Zhenjie Yan on 2018/1/14.
 */
class LocationCoarseTest implements PermissionTest {

    private Context mContext;

    LocationCoarseTest(Context context) {
        this.mContext = context;
    }

    @Override
    public boolean test() throws Throwable {
        LocationManager locationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
        List<String> providers = locationManager.getProviders(true);
        boolean networkProvider = providers.contains(LocationManager.NETWORK_PROVIDER);
        if (networkProvider) {
            return true;
        }

        PackageManager packageManager = mContext.getPackageManager();
        boolean networkHardware = packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_NETWORK);
        if (!networkHardware) return true;

        return !locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/LocationFineTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.content.pm.PackageManager;
import android.location.LocationManager;

import java.util.List;

/**
 * Created by Zhenjie Yan on 2018/1/14.
 */
class LocationFineTest implements PermissionTest {

    private Context mContext;

    LocationFineTest(Context context) {
        this.mContext = context;
    }

    @Override
    public boolean test() throws Throwable {
        LocationManager locationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
        List<String> providers = locationManager.getProviders(true);
        boolean gpsProvider = providers.contains(LocationManager.GPS_PROVIDER);
        boolean passiveProvider = providers.contains(LocationManager.PASSIVE_PROVIDER);
        if (gpsProvider || passiveProvider) {
            return true;
        }

        PackageManager packageManager = mContext.getPackageManager();
        boolean gpsHardware = packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS);
        if (!gpsHardware) return true;

        return !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PermissionChecker.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;

import java.util.List;

/**
 * Created by Zhenjie Yan on 2018/1/7.
 */
public interface PermissionChecker {

    /**
     * Check if the calling context has a set of permissions.
     *
     * @param context {@link Context}.
     * @param permissions one or more permissions.
     *
     * @return true, other wise is false.
     */
    boolean hasPermission(Context context, String... permissions);

    /**
     * Check if the calling context has a set of permissions.
     *
     * @param context {@link Context}.
     * @param permissions one or more permissions.
     *
     * @return true, other wise is false.
     */
    boolean hasPermission(Context context, List<String> permissions);

}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PermissionTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.database.Cursor;

/**
 * Created by Zhenjie Yan on 2018/1/14.
 */
interface PermissionTest {

    boolean test() throws Throwable;

    class CursorTest {

        public static void read(Cursor cursor) {
            int count = cursor.getCount();
            if (count > 0) {
                cursor.moveToFirst();
                int type = cursor.getType(0);
                switch (type) {
                    case Cursor.FIELD_TYPE_BLOB:
                    case Cursor.FIELD_TYPE_NULL: {
                        break;
                    }
                    case Cursor.FIELD_TYPE_INTEGER:
                    case Cursor.FIELD_TYPE_FLOAT:
                    case Cursor.FIELD_TYPE_STRING:
                    default: {
                        cursor.getString(0);
                        break;
                    }
                }
            }
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PhoneStateReadTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.telephony.TelephonyManager;

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class PhoneStateReadTest implements PermissionTest {

    private Context mContext;

    PhoneStateReadTest(Context context) {
        this.mContext = context;
    }

    @Override
    public boolean test() throws Throwable {
        PackageManager packageManager = mContext.getPackageManager();
        if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) return true;

        TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) telephonyManager.getDeviceId();
        else telephonyManager.getDeviceSoftwareVersion();
        return true;
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/RecordAudioTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Created by Zhenjie Yan on 2018/1/14.
 */
class RecordAudioTest implements PermissionTest {

    private static final int[] RATES = new int[]{8000, 11025, 22050, 44100};

    private Context mContext;

    RecordAudioTest(Context context) {
        this.mContext = context;
    }

    @Override
    public boolean test() throws Throwable {
        AudioRecord audioRecord = null;
        File file = null;
        FileOutputStream fos = null;
        try {
            int[] params = findAudioParameters();
            if (params == null) return !existMicrophone(mContext);

            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, params[0], params[1], params[2], params[3]);
            int state = audioRecord.getState();
            if (state != AudioRecord.STATE_INITIALIZED) return !existMicrophone(mContext);

            int recordState = audioRecord.getRecordingState();
            if (recordState != AudioRecord.RECORDSTATE_STOPPED) return true;

            audioRecord.startRecording();
            if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) return true;

            File cacheDir = new File(mContext.getCacheDir(), "_andpermission_audio_record_test_");
            cacheDir.mkdirs();

            file = new File(cacheDir, Long.toString(System.currentTimeMillis()));
            if (file.exists()) file.createNewFile();

            fos = new FileOutputStream(file);
            byte[] buffer = new byte[params[3]];

            int len = audioRecord.read(buffer, 0, params[3]);
            fos.write(buffer, 0, len);
            fos.flush();
        } catch (Throwable e) {
            return !existMicrophone(mContext);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ignored) {
                }
            }
            if (file != null && file.exists()) {
                file.delete();
            }
            if (audioRecord != null) {
                audioRecord.release();
            }
        }
        return true;
    }

    public static boolean existMicrophone(Context context) {
        PackageManager packageManager = context.getPackageManager();
        return packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE);
    }

    public static int[] findAudioParameters() {
        for (int rate : RATES) {
            for (int channel : new int[]{AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO}) {
                for (int format : new int[]{AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT}) {
                    int buffer = AudioRecord.getMinBufferSize(rate, channel, format);
                    if (buffer != AudioRecord.ERROR_BAD_VALUE) {
                        return new int[]{rate, channel, format, buffer};
                    }
                }
            }
        }
        return null;
    }

}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SensorActivityTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class SensorActivityTest implements PermissionTest {

    private Context mContext;

    SensorActivityTest(Context context) {
        this.mContext = context;
    }

    @Override
    public boolean test() throws Throwable {
        SensorManager sensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        try {
            Sensor heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
            sensorManager.registerListener(SENSOR_EVENT_LISTENER, heartRateSensor, 3);
            sensorManager.unregisterListener(SENSOR_EVENT_LISTENER, heartRateSensor);
        } catch (Throwable e) {
            PackageManager packageManager = mContext.getPackageManager();
            return !packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
        }
        return true;
    }

    private static final SensorEventListener SENSOR_EVENT_LISTENER = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }
    };
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SensorHeartTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class SensorHeartTest implements PermissionTest {

    private Context mContext;

    SensorHeartTest(Context context) {
        this.mContext = context;
    }

    @Override
    public boolean test() throws Throwable {
        SensorManager sensorManager = (SensorManager)mContext.getSystemService(Context.SENSOR_SERVICE);
        try {
            Sensor heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
            sensorManager.registerListener(SENSOR_EVENT_LISTENER, heartRateSensor, 3);
            sensorManager.unregisterListener(SENSOR_EVENT_LISTENER, heartRateSensor);
        } catch (Throwable e) {
            PackageManager packageManager = mContext.getPackageManager();
            return !packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_HEART_RATE);
        }
        return true;
    }

    private static final SensorEventListener SENSOR_EVENT_LISTENER = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }
    };
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SipTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;

import com.yanzhenjie.permission.util.StringUtils;

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class SipTest implements PermissionTest {

    private static final String USER = StringUtils.hexToText("5065726D697373696F6E");
    private static final String IP = StringUtils.hexToText("3132372E302E302E31");
    private static final String PASSWORD = StringUtils.textToHex("70617373776F7264");

    private Context mContext;

    SipTest(Context context) {
        this.mContext = context;
    }

    @Override
    public boolean test() throws Throwable {
        if (!SipManager.isApiSupported(mContext)) {
            return true;
        }
        SipManager manager = SipManager.newInstance(mContext);
        if (manager == null) {
            return true;
        }
        SipProfile.Builder builder = new SipProfile.Builder(USER, IP);
        builder.setPassword(PASSWORD);
        SipProfile profile = builder.build();
        manager.open(profile);
        manager.close(profile.getUriString());
        return true;
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SmsReadTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.Telephony;

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class SmsReadTest implements PermissionTest {

    private ContentResolver mResolver;

    SmsReadTest(Context context) {
        mResolver = context.getContentResolver();
    }

    @Override
    public boolean test() throws Throwable {
        String[] projection = new String[] {Telephony.Sms._ID, Telephony.Sms.ADDRESS, Telephony.Sms.PERSON,
            Telephony.Sms.BODY};
        Cursor cursor = mResolver.query(Telephony.Sms.CONTENT_URI, projection, null, null, null);
        if (cursor != null) {
            try {
                CursorTest.read(cursor);
            } finally {
                cursor.close();
            }
            return true;
        } else {
            return false;
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StandardChecker.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.text.TextUtils;

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

/**
 * Created by Zhenjie Yan on 2018/1/7.
 */
public final class StandardChecker implements PermissionChecker {

    private static final int MODE_ASK = 4;
    private static final int MODE_COMPAT = 5;

    public StandardChecker() {
    }

    @Override
    public boolean hasPermission(Context context, String... permissions) {
        return hasPermission(context, Arrays.asList(permissions));
    }

    @Override
    public boolean hasPermission(Context context, List<String> permissions) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;

        AppOpsManager opsManager = null;
        for (String permission : permissions) {
            int result = context.checkPermission(permission, android.os.Process.myPid(), android.os.Process.myUid());
            if (result == PackageManager.PERMISSION_DENIED) {
                return false;
            }

            String op = AppOpsManager.permissionToOp(permission);
            if (TextUtils.isEmpty(op)) {
                continue;
            }

            if (opsManager == null) opsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            result = opsManager.checkOpNoThrow(op, android.os.Process.myUid(), context.getPackageName());
            if (result != AppOpsManager.MODE_ALLOWED && result != MODE_ASK && result != MODE_COMPAT) {
                return false;
            }
        }
        return true;
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StorageReadTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;

import java.io.File;

/**
 * Created by Zhenjie Yan on 2018/1/16.
 */
class StorageReadTest implements PermissionTest {

    StorageReadTest() {
    }

    @Override
    public boolean test() throws Throwable {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !Environment.isExternalStorageLegacy()) return true;

        if (!TextUtils.equals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState())) return true;

        File directory = Environment.getExternalStorageDirectory();
        if (!directory.exists()) return true;

        long modified = directory.lastModified();
        String[] pathList = directory.list();
        return modified > 0 && pathList != null;
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StorageWriteTest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;

import java.io.File;

/**
 * Created by Zhenjie Yan on 2018/1/16.
 */
class StorageWriteTest implements PermissionTest {

    private Context mContext;

    StorageWriteTest(Context c) {
        mContext = c;
    }

    @Override
    public boolean test() throws Throwable {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !Environment.isExternalStorageLegacy()) return true;

        if (!TextUtils.equals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState())) return true;

        File directory = mContext.getExternalFilesDir(null);

        if (!directory.exists()) return true;

        File parent = new File(directory, "Android");
        if (parent.exists() && parent.isFile()) {
            if (!parent.delete()) return false;
        }
        if (!parent.exists()) {
            if (!parent.mkdirs()) return false;
        }
        File file = new File(parent, "ANDROID.PERMISSION.TEST");
        if (file.exists()) {
            return file.delete();
        } else {
            return file.createNewFile();
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StrictChecker.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.checker;

import android.content.ContentResolver;
import android.content.Context;
import android.os.Build;

import com.yanzhenjie.permission.runtime.Permission;

import java.util.List;

/**
 * Created by Zhenjie Yan on 2018/1/7.
 */
public final class StrictChecker implements PermissionChecker {

    public StrictChecker() {
    }

    @Override
    public boolean hasPermission(Context context, String... permissions) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return true;

        for (String permission : permissions) {
            if (!hasPermission(context, permission)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean hasPermission(Context context, List<String> permissions) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return true;

        for (String permission : permissions) {
            if (!hasPermission(context, permission)) {
                return false;
            }
        }
        return true;
    }

    private boolean hasPermission(Context context, String permission) {
        try {
            switch (permission) {
                case Permission.READ_CALENDAR:
                    return checkReadCalendar(context);
                case Permission.WRITE_CALENDAR:
                    return checkWriteCalendar(context);
                case Permission.CAMERA:
                    return checkCamera(context);
                case Permission.READ_CONTACTS:
                    return checkReadContacts(context);
                case Permission.WRITE_CONTACTS:
                    return checkWriteContacts(context);
                case Permission.GET_ACCOUNTS:
                    return true;
                case Permission.ACCESS_COARSE_LOCATION:
                    return checkCoarseLocation(context);
                case Permission.ACCESS_FINE_LOCATION:
                    return checkFineLocation(context);
                case Permission.RECORD_AUDIO:
                    return checkRecordAudio(context);
                case Permission.READ_PHONE_STATE:
                    return checkReadPhoneState(context);
                case Permission.CALL_PHONE:
                    return true;
                case Permission.READ_CALL_LOG:
                    return checkReadCallLog(context);
                case Permission.WRITE_CALL_LOG:
                    return checkWriteCallLog(context);
                case Permission.ADD_VOICEMAIL:
                    return true;
                case Permission.USE_SIP:
                    return checkSip(context);
                case Permission.PROCESS_OUTGOING_CALLS:
                    return true;
                case Permission.BODY_SENSORS:
                    return checkSensorHeart(context);
                case Permission.ACTIVITY_RECOGNITION:
                    return checkSensorActivity(context);
                case Permission.SEND_SMS:
                case Permission.RECEIVE_MMS:
                    return true;
                case Permission.READ_SMS:
                    return checkReadSms(context);
                case Permission.RECEIVE_WAP_PUSH:
                case Permission.RECEIVE_SMS:
                    return true;
                case Permission.READ_EXTERNAL_STORAGE:
                    return checkReadStorage();
                case Permission.WRITE_EXTERNAL_STORAGE:
                    return checkWriteStorage(context);
            }
        } catch (Throwable e) {
            return false;
        }
        return true;
    }

    private static boolean checkReadCalendar(Context context) throws Throwable {
        PermissionTest test = new CalendarReadTest(context);
        return test.test();
    }

    private static boolean checkWriteCalendar(Context context) throws Throwable {
        PermissionTest test = new CalendarWriteTest(context);
        return test.test();
    }

    private static boolean checkCamera(Context context) throws Throwable {
        PermissionTest test = new CameraTest(context);
        return test.test();
    }

    private static boolean checkReadContacts(Context context) throws Throwable {
        PermissionTest test = new ContactsReadTest(context);
        return test.test();
    }

    private static boolean checkWriteContacts(Context context) throws Throwable {
        ContentResolver resolver = context.getContentResolver();
        PermissionTest test = new ContactsWriteTest(resolver);
        return test.test();
    }

    private static boolean checkCoarseLocation(Context context) throws Throwable {
        PermissionTest test = new LocationCoarseTest(context);
        return test.test();
    }

    private static boolean checkFineLocation(Context context) throws Throwable {
        PermissionTest test = new LocationFineTest(context);
        return test.test();
    }

    private static boolean checkRecordAudio(Context context) throws Throwable {
        PermissionTest test = new RecordAudioTest(context);
        return test.test();
    }

    private static boolean checkReadPhoneState(Context context) throws Throwable {
        PermissionTest test = new PhoneStateReadTest(context);
        return test.test();
    }

    private static boolean checkReadCallLog(Context context) throws Throwable {
        PermissionTest test = new CallLogReadTest(context);
        return test.test();
    }

    private static boolean checkWriteCallLog(Context context) throws Throwable {
        PermissionTest test = new CallLogWriteTest(context);
        return test.test();
    }

    private static boolean checkSip(Context context) throws Throwable {
        PermissionTest test = new SipTest(context);
        return test.test();
    }

    private static boolean checkSensorHeart(Context context) throws Throwable {
        PermissionTest test = new SensorHeartTest(context);
        return test.test();
    }

    private static boolean checkSensorActivity(Context context) throws Throwable {
        PermissionTest test = new SensorActivityTest(context);
        return test.test();
    }

    private static boolean checkReadSms(Context context) throws Throwable {
        PermissionTest test = new SmsReadTest(context);
        return test.test();
    }

    private static boolean checkReadStorage() throws Throwable {
        PermissionTest test = new StorageReadTest();
        return test.test();
    }

    private static boolean checkWriteStorage(Context context) throws Throwable {
        PermissionTest test = new StorageWriteTest(context);
        return test.test();
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/install/BaseRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.install;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.AndPermission;
import com.yanzhenjie.permission.Rationale;
import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.source.Source;

import java.io.File;

/**
 * Created by Zhenjie Yan on 2018/6/1.
 */
abstract class BaseRequest implements InstallRequest {

    private Source mSource;

    private File mFile;
    private Rationale<File> mRationale = new Rationale<File>() {
        @Override
        public void showRationale(Context context, File data, RequestExecutor executor) {
            executor.execute();
        }
    };
    private Action<File> mGranted;
    private Action<File> mDenied;

    BaseRequest(Source source) {
        this.mSource = source;
    }

    @Override
    public final InstallRequest file(File file) {
        this.mFile = file;
        return this;
    }

    @Override
    public final InstallRequest rationale(Rationale<File> rationale) {
        this.mRationale = rationale;
        return this;
    }

    @Override
    public final InstallRequest onGranted(Action<File> granted) {
        this.mGranted = granted;
        return this;
    }

    @Override
    public final InstallRequest onDenied(Action<File> denied) {
        this.mDenied = denied;
        return this;
    }

    /**
     * Why permissions are required.
     */
    final void showRationale(RequestExecutor executor) {
        mRationale.showRationale(mSource.getContext(), null, executor);
    }

    /**
     * Start the installation.
     */
    final void install() {
        if (mFile != null) {
            Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri uri = AndPermission.getFileUri(mSource.getContext(), mFile);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
            mSource.startActivity(intent);
        }
    }

    /**
     * Callback acceptance status.
     */
    final void callbackSucceed() {
        if (mGranted != null) {
            mGranted.onAction(mFile);
        }
    }

    /**
     * Callback rejected state.
     */
    final void callbackFailed() {
        if (mDenied != null) {
            mDenied.onAction(mFile);
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/install/InstallRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.install;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.Rationale;

import java.io.File;

/**
 * Created by Zhenjie Yan on 2018/4/28.
 */
public interface InstallRequest {

    /**
     * The apk file.
     *
     * @param file apk file.
     */
    InstallRequest file(File file);

    /**
     * Set request rationale.
     */
    InstallRequest rationale(Rationale<File> rationale);

    /**
     * Action to be taken when all permissions are granted.
     */
    InstallRequest onGranted(Action<File> granted);

    /**
     * Action to be taken when all permissions are denied.
     */
    InstallRequest onDenied(Action<File> denied);

    /**
     * Start install.
     */
    void start();
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/install/NRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.install;

import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/4/28.
 */
class NRequest extends BaseRequest {

    NRequest(Source source) {
        super(source);
    }

    @Override
    public void start() {
        callbackSucceed();
        install();
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/install/NRequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.install;

import com.yanzhenjie.permission.Boot;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/4/28.
 */
public class NRequestFactory implements Boot.InstallRequestFactory {

    @Override
    public InstallRequest create(Source source) {
        return new NRequest(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/install/ORequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.install;

import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.bridge.BridgeRequest;
import com.yanzhenjie.permission.bridge.RequestManager;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/4/28.
 */
class ORequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback {

    private Source mSource;

    ORequest(Source source) {
        super(source);
        this.mSource = source;
    }

    @Override
    public void start() {
        if (mSource.canRequestPackageInstalls()) {
            callbackSucceed();
            install();
        } else {
            showRationale(this);
        }
    }

    @Override
    public void execute() {
        BridgeRequest request = new BridgeRequest(mSource);
        request.setType(BridgeRequest.TYPE_INSTALL);
        request.setCallback(this);
        RequestManager.get().add(request);
    }

    @Override
    public void cancel() {
        callbackFailed();
    }

    @Override
    public void onCallback() {
        if (mSource.canRequestPackageInstalls()) {
            callbackSucceed();
            install();
        } else {
            callbackFailed();
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/install/ORequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.install;

import com.yanzhenjie.permission.Boot;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/4/28.
 */
public class ORequestFactory implements Boot.InstallRequestFactory {

    @Override
    public InstallRequest create(Source source) {
        return new ORequest(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/BaseRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify;

import android.content.Context;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.Rationale;
import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/6/1.
 */
abstract class BaseRequest implements PermissionRequest {

    private Source mSource;

    private Rationale<Void> mRationale = new Rationale<Void>() {
        @Override
        public void showRationale(Context context, Void data, RequestExecutor executor) {
            executor.execute();
        }
    };
    private Action<Void> mGranted;
    private Action<Void> mDenied;

    BaseRequest(Source source) {
        this.mSource = source;
    }

    @Override
    public final PermissionRequest rationale(Rationale<Void> rationale) {
        this.mRationale = rationale;
        return this;
    }

    @Override
    public final PermissionRequest onGranted(Action<Void> granted) {
        this.mGranted = granted;
        return this;
    }

    @Override
    public final PermissionRequest onDenied(Action<Void> denied) {
        this.mDenied = denied;
        return this;
    }

    /**
     * Why permissions are required.
     */
    final void showRationale(RequestExecutor executor) {
        mRationale.showRationale(mSource.getContext(), null, executor);
    }

    /**
     * Callback acceptance status.
     */
    final void callbackSucceed() {
        if (mGranted != null) {
            mGranted.onAction(null);
        }
    }

    /**
     * Callback rejected state.
     */
    final void callbackFailed() {
        if (mDenied != null) {
            mDenied.onAction(null);
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/NRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify;

import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.bridge.BridgeRequest;
import com.yanzhenjie.permission.bridge.RequestManager;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
class NRequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback {

    private Source mSource;

    NRequest(Source source) {
        super(source);
        this.mSource = source;
    }

    @Override
    public void start() {
        if (mSource.canNotify()) {
            callbackSucceed();
        } else {
            showRationale(this);
        }
    }

    @Override
    public void execute() {
        BridgeRequest request = new BridgeRequest(mSource);
        request.setType(BridgeRequest.TYPE_APP_DETAILS);
        request.setCallback(this);
        RequestManager.get().add(request);
    }

    @Override
    public void cancel() {
        callbackFailed();
    }

    @Override
    public void onCallback() {
        if (mSource.canNotify()) {
            callbackSucceed();
        } else {
            callbackFailed();
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/NRequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify;

import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
public class NRequestFactory implements Notify.PermissionRequestFactory {

    @Override
    public PermissionRequest create(Source source) {
        return new NRequest(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/Notify.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify;

import android.os.Build;

import com.yanzhenjie.permission.notify.listener.J1RequestFactory;
import com.yanzhenjie.permission.notify.listener.J2RequestFactory;
import com.yanzhenjie.permission.notify.listener.ListenerRequest;
import com.yanzhenjie.permission.notify.option.NotifyOption;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2/22/19.
 */
public class Notify implements NotifyOption {

    private static final PermissionRequestFactory PERMISSION_REQUEST_FACTORY;
    private static final ListenerRequestFactory LISTENER_REQUEST_FACTORY;

    static {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            PERMISSION_REQUEST_FACTORY = new ORequestFactory();
        } else {
            PERMISSION_REQUEST_FACTORY = new NRequestFactory();
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            LISTENER_REQUEST_FACTORY = new J2RequestFactory();
        } else {
            LISTENER_REQUEST_FACTORY = new J1RequestFactory();
        }
    }

    public interface PermissionRequestFactory {

        /**
         * Create notify request.
         */
        PermissionRequest create(Source source);
    }

    public interface ListenerRequestFactory {

        /**
         * Create notification listener request.
         */
        ListenerRequest create(Source source);
    }

    private Source mSource;

    public Notify(Source source) {
        this.mSource = source;
    }

    public PermissionRequest permission() {
        return PERMISSION_REQUEST_FACTORY.create(mSource);
    }

    public ListenerRequest listener() {
        return LISTENER_REQUEST_FACTORY.create(mSource);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/ORequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify;

import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.bridge.BridgeRequest;
import com.yanzhenjie.permission.bridge.RequestManager;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
class ORequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback {

    private Source mSource;

    ORequest(Source source) {
        super(source);
        this.mSource = source;
    }

    @Override
    public void start() {
        if (mSource.canNotify()) {
            callbackSucceed();
        } else {
            showRationale(this);
        }
    }

    @Override
    public void execute() {
        BridgeRequest request = new BridgeRequest(mSource);
        request.setType(BridgeRequest.TYPE_NOTIFY);
        request.setCallback(this);
        RequestManager.get().add(request);
    }

    @Override
    public void cancel() {
        callbackFailed();
    }

    @Override
    public void onCallback() {
        if (mSource.canNotify()) {
            callbackSucceed();
        } else {
            callbackFailed();
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/ORequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify;

import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
public class ORequestFactory implements Notify.PermissionRequestFactory {

    @Override
    public PermissionRequest create(Source source) {
        return new ORequest(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/PermissionRequest.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.Rationale;

/**
 * Created by Zhenjie Yan on 2/14/19.
 */
public interface PermissionRequest {

    /**
     * Set request rationale.
     */
    PermissionRequest rationale(Rationale<Void> rationale);

    /**
     * Action to be taken when all permissions are granted.
     */
    PermissionRequest onGranted(Action<Void> granted);

    /**
     * Action to be taken when all permissions are denied.
     */
    PermissionRequest onDenied(Action<Void> denied);

    /**
     * Start install.
     */
    void start();

}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/BaseRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify.listener;

import android.content.Context;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.Rationale;
import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/6/1.
 */
abstract class BaseRequest implements ListenerRequest {

    private Source mSource;

    private Rationale<Void> mRationale = new Rationale<Void>() {
        @Override
        public void showRationale(Context context, Void data, RequestExecutor executor) {
            executor.execute();
        }
    };
    private Action<Void> mGranted;
    private Action<Void> mDenied;

    BaseRequest(Source source) {
        this.mSource = source;
    }

    @Override
    public final ListenerRequest rationale(Rationale<Void> rationale) {
        this.mRationale = rationale;
        return this;
    }

    @Override
    public final ListenerRequest onGranted(Action<Void> granted) {
        this.mGranted = granted;
        return this;
    }

    @Override
    public final ListenerRequest onDenied(Action<Void> denied) {
        this.mDenied = denied;
        return this;
    }

    /**
     * Why permissions are required.
     */
    final void showRationale(RequestExecutor executor) {
        mRationale.showRationale(mSource.getContext(), null, executor);
    }

    /**
     * Callback acceptance status.
     */
    final void callbackSucceed() {
        if (mGranted != null) {
            mGranted.onAction(null);
        }
    }

    /**
     * Callback rejected state.
     */
    final void callbackFailed() {
        if (mDenied != null) {
            mDenied.onAction(null);
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J1Request.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify.listener;

import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
class J1Request extends BaseRequest implements RequestExecutor {

    J1Request(Source source) {
        super(source);
    }

    @Override
    public void start() {
        callbackSucceed();
    }

    @Override
    public void execute() {
        // Nothing.
    }

    @Override
    public void cancel() {
        // Nothing.
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J1RequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify.listener;

import com.yanzhenjie.permission.notify.Notify;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
public class J1RequestFactory implements Notify.ListenerRequestFactory {

    @Override
    public ListenerRequest create(Source source) {
        return new J1Request(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J2Request.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify.listener;

import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.bridge.BridgeRequest;
import com.yanzhenjie.permission.bridge.RequestManager;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
class J2Request extends BaseRequest implements RequestExecutor, BridgeRequest.Callback {

    private Source mSource;

    J2Request(Source source) {
        super(source);
        this.mSource = source;
    }

    @Override
    public void start() {
        if (mSource.canListenerNotification()) {
            callbackSucceed();
        } else {
            showRationale(this);
        }
    }

    @Override
    public void execute() {
        BridgeRequest request = new BridgeRequest(mSource);
        request.setType(BridgeRequest.TYPE_NOTIFY_LISTENER);
        request.setCallback(this);
        RequestManager.get().add(request);
    }

    @Override
    public void cancel() {
        callbackFailed();
    }

    @Override
    public void onCallback() {
        if (mSource.canListenerNotification()) {
            callbackSucceed();
        } else {
            callbackFailed();
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J2RequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify.listener;

import com.yanzhenjie.permission.notify.Notify;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
public class J2RequestFactory implements Notify.ListenerRequestFactory {

    @Override
    public ListenerRequest create(Source source) {
        return new J2Request(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/ListenerRequest.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify.listener;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.Rationale;

/**
 * Created by Zhenjie Yan on 2/14/19.
 */
public interface ListenerRequest {

    /**
     * Set request rationale.
     */
    ListenerRequest rationale(Rationale<Void> rationale);

    /**
     * Action to be taken when all permissions are granted.
     */
    ListenerRequest onGranted(Action<Void> granted);

    /**
     * Action to be taken when all permissions are denied.
     */
    ListenerRequest onDenied(Action<Void> denied);

    /**
     * Start install.
     */
    void start();

}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/notify/option/NotifyOption.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.notify.option;

import com.yanzhenjie.permission.notify.PermissionRequest;
import com.yanzhenjie.permission.notify.listener.ListenerRequest;

/**
 * Created by Zhenjie Yan on 2/14/19.
 */
public interface NotifyOption {

    /**
     * Handle permissions.
     */
    PermissionRequest permission();

    /**
     * Handle notify listener.
     */
    ListenerRequest listener();
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/option/Option.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.option;

import com.yanzhenjie.permission.install.InstallRequest;
import com.yanzhenjie.permission.notify.option.NotifyOption;
import com.yanzhenjie.permission.overlay.OverlayRequest;
import com.yanzhenjie.permission.runtime.option.RuntimeOption;
import com.yanzhenjie.permission.setting.Setting;

/**
 * Created by Zhenjie Yan on 2/22/19.
 */
public interface Option {

    /**
     * Handle runtime permissions.
     */
    RuntimeOption runtime();

    /**
     * Handle request package install permission.
     */
    InstallRequest install();

    /**
     * Handle overlay permission.
     */
    OverlayRequest overlay();

    /**
     * Handle notification permission.
     */
    NotifyOption notification();

    /**
     * Handle system setting.
     */
    Setting setting();
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/BaseRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.overlay;

import android.app.Dialog;
import android.content.Context;
import android.os.Build;
import android.view.Window;
import android.view.WindowManager;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.R;
import com.yanzhenjie.permission.Rationale;
import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/6/1.
 */
abstract class BaseRequest implements OverlayRequest {

    private Source mSource;

    private Rationale<Void> mRationale = new Rationale<Void>() {
        @Override
        public void showRationale(Context context, Void data, RequestExecutor executor) {
            executor.execute();
        }
    };
    private Action<Void> mGranted;
    private Action<Void> mDenied;

    BaseRequest(Source source) {
        this.mSource = source;
    }

    @Override
    public final OverlayRequest rationale(Rationale<Void> rationale) {
        this.mRationale = rationale;
        return this;
    }

    @Override
    public final OverlayRequest onGranted(Action<Void> granted) {
        this.mGranted = granted;
        return this;
    }

    @Override
    public final OverlayRequest onDenied(Action<Void> denied) {
        this.mDenied = denied;
        return this;
    }

    /**
     * Why permissions are required.
     */
    final void showRationale(RequestExecutor executor) {
        mRationale.showRationale(mSource.getContext(), null, executor);
    }

    /**
     * Callback acceptance status.
     */
    final void callbackSucceed() {
        if (mGranted != null) {
            mGranted.onAction(null);
        }
    }

    /**
     * Callback rejected state.
     */
    final void callbackFailed() {
        if (mDenied != null) {
            mDenied.onAction(null);
        }
    }

    static boolean tryDisplayDialog(Context context) {
        Dialog dialog = new Dialog(context, R.style.Permission_Theme_Dialog_Transparent);
        Window window = dialog.getWindow();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
        } else {
            window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        }
        try {
            dialog.show();
        } catch (Exception e) {
            return false;
        } finally {
            if (dialog.isShowing()) dialog.dismiss();
        }
        return true;
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/LRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.overlay;

import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.bridge.BridgeRequest;
import com.yanzhenjie.permission.bridge.RequestManager;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
class LRequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback {

    private Source mSource;

    LRequest(Source source) {
        super(source);
        this.mSource = source;
    }

    @Override
    public void start() {
        if (tryDisplayDialog(mSource.getContext())) {
            callbackSucceed();
        } else {
            showRationale(this);
        }
    }

    @Override
    public void execute() {
        BridgeRequest request = new BridgeRequest(mSource);
        request.setType(BridgeRequest.TYPE_ALERT_WINDOW);
        request.setCallback(this);
        RequestManager.get().add(request);
    }

    @Override
    public void cancel() {
        callbackFailed();
    }

    @Override
    public void onCallback() {
        if (tryDisplayDialog(mSource.getContext())) {
            callbackSucceed();
        } else {
            callbackFailed();
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/LRequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.overlay;

import com.yanzhenjie.permission.Boot;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
public class LRequestFactory implements Boot.OverlayRequestFactory {

    @Override
    public OverlayRequest create(Source source) {
        return new LRequest(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/MRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.overlay;

import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.bridge.BridgeRequest;
import com.yanzhenjie.permission.bridge.RequestManager;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
class MRequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback {

    private Source mSource;

    MRequest(Source source) {
        super(source);
        this.mSource = source;
    }

    @Override
    public void start() {
        if (mSource.canDrawOverlays()) {
            onCallback();
        } else {
            showRationale(this);
        }
    }

    @Override
    public void execute() {
        BridgeRequest request = new BridgeRequest(mSource);
        request.setType(BridgeRequest.TYPE_OVERLAY);
        request.setCallback(this);
        RequestManager.get().add(request);
    }

    @Override
    public void cancel() {
        callbackFailed();
    }

    @Override
    public void onCallback() {
        if (mSource.canDrawOverlays() && tryDisplayDialog(mSource.getContext())) {
            callbackSucceed();
        } else {
            callbackFailed();
        }
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/MRequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.overlay;

import com.yanzhenjie.permission.Boot;
import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
public class MRequestFactory implements Boot.OverlayRequestFactory {

    @Override
    public OverlayRequest create(Source source) {
        return new MRequest(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/OverlayRequest.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.overlay;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.Rationale;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
public interface OverlayRequest {

    /**
     * Set request rationale.
     */
    OverlayRequest rationale(Rationale<Void> rationale);

    /**
     * Action to be taken when all permissions are granted.
     */
    OverlayRequest onGranted(Action<Void> granted);

    /**
     * Action to be taken when all permissions are denied.
     */
    OverlayRequest onDenied(Action<Void> denied);

    /**
     * Start request.
     */
    void start();
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/setting/LSettingPage.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.overlay.setting;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;

import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/29.
 */
public class LSettingPage {

    private static final String MARK = Build.MANUFACTURER.toLowerCase();

    private Source mSource;

    public LSettingPage(Source source) {
        this.mSource = source;
    }

    public void start(int requestCode) {
        Intent intent;
        if (MARK.contains("huawei")) {
            intent = huaweiApi(mSource.getContext());
        } else if (MARK.contains("xiaomi")) {
            intent = xiaomiApi(mSource.getContext());
        } else if (MARK.contains("oppo")) {
            intent = oppoApi(mSource.getContext());
        } else if (MARK.contains("vivo")) {
            intent = vivoApi(mSource.getContext());
        } else if (MARK.contains("meizu")) {
            intent = meizuApi(mSource.getContext());
        } else {
            intent = defaultApi(mSource.getContext());
        }

        try {
            mSource.startActivityForResult(intent, requestCode);
        } catch (Exception e) {
            intent = defaultApi(mSource.getContext());
            mSource.startActivityForResult(intent, requestCode);
        }
    }

    private static Intent defaultApi(Context context) {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.fromParts("package", context.getPackageName(), null));
        return intent;
    }

    private Intent huaweiApi(Context context) {
        Intent intent = new Intent();
        intent.putExtra("package", context.getPackageName());
        intent.putExtra("packageName", context.getPackageName());
        intent.setData(Uri.fromParts("package", context.getPackageName(), null));
        intent.setClassName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity");
        if (hasActivity(context, intent)) return intent;

        intent.setClassName("com.huawei.systemmanager", "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");
        if (hasActivity(context, intent)) return intent;

        intent.setClassName("com.huawei.systemmanager", "com.huawei.notificationmanager.ui.NotificationManagmentActivity");
        if (hasActivity(context, intent)) return intent;

        return defaultApi(context);
    }

    private Intent xiaomiApi(Context context) {
        Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
        intent.putExtra("extra_pkgname", context.getPackageName());
        if (hasActivity(context, intent)) return intent;

        intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
        if (hasActivity(context, intent)) return intent;

        return defaultApi(context);
    }

    private Intent oppoApi(Context context) {
        Intent intent = new Intent();
        intent.putExtra("packageName", context.getPackageName());
        intent.setClassName("com.color.safecenter",
            "com.color.safecenter.permission.floatwindow.FloatWindowListActivity");
        if (hasActivity(context, intent)) return intent;

        intent.setClassName("com.coloros.safecenter", "com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity");
        if (hasActivity(context, intent)) return intent;

        intent.setClassName("com.oppo.safe", "com.oppo.safe.permission.PermissionAppListActivity");
        if (hasActivity(context, intent)) return intent;

        return defaultApi(context);
    }

    private Intent vivoApi(Context context) {
        Intent intent = new Intent();
        intent.setClassName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.FloatWindowManager");
        intent.putExtra("packagename", context.getPackageName());
        if (hasActivity(context, intent)) return intent;

        intent.setClassName("com.iqoo.secure", "com.iqoo.secure.safeguard.SoftPermissionDetailActivity");
        if (hasActivity(context, intent)) return intent;

        return defaultApi(context);
    }

    private Intent meizuApi(Context context) {
        Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
        intent.putExtra("packageName", context.getPackageName());
        intent.setComponent(new ComponentName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity"));
        if (hasActivity(context, intent)) return intent;

        return defaultApi(context);
    }

    private static boolean hasActivity(Context context, Intent intent) {
        PackageManager packageManager = context.getPackageManager();
        return packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/setting/MSettingPage.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.overlay.setting;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;

import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/5/30.
 */
public class MSettingPage {

    private static final String MARK = Build.MANUFACTURER.toLowerCase();

    private Source mSource;

    public MSettingPage(Source source) {
        this.mSource = source;
    }

    public void start(int requestCode) {
        Intent intent;
        if (MARK.contains("meizu")) {
            intent = meiZuApi(mSource.getContext());
        } else {
            intent = defaultApi(mSource.getContext());
        }

        try {
            mSource.startActivityForResult(intent, requestCode);
        } catch (Exception e) {
            intent = appDetailsApi(mSource.getContext());
            mSource.startActivityForResult(intent, requestCode);
        }
    }

    private static Intent appDetailsApi(Context context) {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.fromParts("package", context.getPackageName(), null));
        return intent;
    }

    private static Intent defaultApi(Context context) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        intent.setData(Uri.fromParts("package", context.getPackageName(), null));
        if (hasActivity(context, intent)) return intent;

        return appDetailsApi(context);
    }

    private static Intent meiZuApi(Context context) {
        Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
        intent.putExtra("packageName", context.getPackageName());
        intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity");
        if (hasActivity(context, intent)) return intent;

        return defaultApi(context);
    }

    private static boolean hasActivity(Context context, Intent intent) {
        PackageManager packageManager = context.getPackageManager();
        return packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/BaseRequest.java
================================================
/*
 * Copyright 2019 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.runtime;

import android.content.Context;
import android.os.Build;

import androidx.annotation.NonNull;

import com.yanzhenjie.permission.Action;
import com.yanzhenjie.permission.Rationale;
import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.checker.PermissionChecker;
import com.yanzhenjie.permission.source.Source;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * Created Zhenjie Yan on 2019-10-10.
 */
abstract class BaseRequest implements PermissionRequest {

    private Source mSource;

    private Rationale<List<String>> mRationale = new Rationale<List<String>>() {
        @Override
        public void showRationale(Context context, List<String> data, RequestExecutor executor) {
            executor.execute();
        }
    };
    private Action<List<String>> mGranted;
    private Action<List<String>> mDenied;

    BaseRequest(Source source) {
        this.mSource = source;
    }

    @Override
    public PermissionRequest rationale(@NonNull Rationale<List<String>> rationale) {
        this.mRationale = rationale;
        return this;
    }

    @Override
    public PermissionRequest onGranted(@NonNull Action<List<String>> granted) {
        this.mGranted = granted;
        return this;
    }

    @Override
    public PermissionRequest onDenied(@NonNull Action<List<String>> denied) {
        this.mDenied = denied;
        return this;
    }

    /**
     * Why permissions are required.
     */
    final void showRationale(List<String> rationaleList, RequestExecutor executor) {
        mRationale.showRationale(mSource.getContext(), rationaleList, executor);
    }

    /**
     * Callback acceptance status.
     */
    final void callbackSucceed(List<String> grantedList) {
        if (mGranted != null) {
            mGranted.onAction(grantedList);
        }
    }

    /**
     * Callback rejected state.
     */
    final void callbackFailed(List<String> deniedList) {
        if (mDenied != null) {
            mDenied.onAction(deniedList);
        }
    }

    /**
     * Filter the permissions you want to apply; remove unsupported and duplicate permissions.
     */
    public static List<String> filterPermissions(List<String> permissions) {
        permissions = new ArrayList<>(new HashSet<>(permissions));
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            permissions.remove(Permission.READ_PHONE_NUMBERS);
            permissions.remove(Permission.ANSWER_PHONE_CALLS);
        }

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            permissions.remove(Permission.ACTIVITY_RECOGNITION);
            permissions.remove(Permission.ACCESS_BACKGROUND_LOCATION);
        }
        return permissions;
    }

    /**
     * Get denied permissions.
     */
    public static List<String> getDeniedPermissions(PermissionChecker checker, Source source, List<String> permissions) {
        List<String> deniedList = new ArrayList<>(1);
        for (String permission : permissions) {
            if (!checker.hasPermission(source.getContext(), permission)) {
                deniedList.add(permission);
            }
        }
        return deniedList;
    }

    /**
     * Get permissions to show rationale.
     */
    public static List<String> getRationalePermissions(Source source, List<String> deniedPermissions) {
        List<String> rationaleList = new ArrayList<>(1);
        for (String permission : deniedPermissions) {
            if (source.isShowRationalePermission(permission)) {
                rationaleList.add(permission);
            }
        }
        return rationaleList;
    }
}


================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/LRequest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.runtime;

import androidx.annotation.NonNull;

import com.yanzhenjie.permission.checker.PermissionChecker;
import com.yanzhenjie.permission.checker.StrictChecker;
import com.yanzhenjie.permission.source.Source;
import com.yanzhenjie.permission.task.TaskExecutor;

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

/**
 * Created by Zhenjie Yan on 2018/1/25.
 */
class LRequest extends BaseRequest {

    private static final PermissionChecker STRICT_CHECKER = new StrictChecker();

    private Source mSource;

    private List<String> mPermissions;

    LRequest(Source source) {
        super(source);
        this.mSource = source;
    }

    @Override
    public PermissionRequest permission(@NonNull String... permissions) {
        mPermissions = new ArrayList<>();
        mPermissions.addAll(Arrays.asList(permissions));
        return this;
    }

    @Override
    public PermissionRequest permission(@NonNull String[]... groups) {
        mPermissions = new ArrayList<>();
        for (String[] group : groups) {
            mPermissions.addAll(Arrays.asList(group));
        }
        return this;
    }

    @Override
    public void start() {
        mPermissions = filterPermissions(mPermissions);

        new TaskExecutor<List<String>>(mSource.getContext()) {
            @Override
            protected List<String> doInBackground(Void... voids) {
                return getDeniedPermissions(STRICT_CHECKER, mSource, mPermissions);
            }

            @Override
            protected void onFinish(List<String> deniedList) {
                if (deniedList.isEmpty()) {
                    callbackSucceed(mPermissions);
                } else {
                    callbackFailed(deniedList);
                }
            }
        }.execute();
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/LRequestFactory.java
================================================
/*
 * Copyright 2018 Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.runtime;

import com.yanzhenjie.permission.source.Source;

/**
 * Created by Zhenjie Yan on 2018/4/28.
 */
public class LRequestFactory implements Runtime.PermissionRequestFactory {

    @Override
    public PermissionRequest create(Source source) {
        return new LRequest(source);
    }
}

================================================
FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/MRequest.java
================================================
/*
 * Copyright © Zhenjie Yan
 *
 * 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.
 */
package com.yanzhenjie.permission.runtime;

import androidx.annotation.NonNull;

import com.yanzhenjie.permission.RequestExecutor;
import com.yanzhenjie.permission.bri
Download .txt
gitextract_hhydg_6p/

├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── build.gradle
├── config.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── permission/
│   ├── .gitignore
│   ├── build.gradle
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── aidl/
│           │   └── com/
│           │       └── yanzhenjie/
│           │           └── permission/
│           │               └── bridge/
│           │                   └── IBridge.aidl
│           ├── java/
│           │   └── com/
│           │       └── yanzhenjie/
│           │           └── permission/
│           │               ├── Action.java
│           │               ├── AndPermission.java
│           │               ├── Boot.java
│           │               ├── FileProvider.java
│           │               ├── Rationale.java
│           │               ├── RequestExecutor.java
│           │               ├── bridge/
│           │               │   ├── BridgeActivity.java
│           │               │   ├── BridgeRequest.java
│           │               │   ├── BridgeService.java
│           │               │   ├── Messenger.java
│           │               │   ├── RequestExecutor.java
│           │               │   └── RequestManager.java
│           │               ├── checker/
│           │               │   ├── CalendarReadTest.java
│           │               │   ├── CalendarWriteTest.java
│           │               │   ├── CallLogReadTest.java
│           │               │   ├── CallLogWriteTest.java
│           │               │   ├── CameraTest.java
│           │               │   ├── ContactsReadTest.java
│           │               │   ├── ContactsWriteTest.java
│           │               │   ├── DoubleChecker.java
│           │               │   ├── LocationCoarseTest.java
│           │               │   ├── LocationFineTest.java
│           │               │   ├── PermissionChecker.java
│           │               │   ├── PermissionTest.java
│           │               │   ├── PhoneStateReadTest.java
│           │               │   ├── RecordAudioTest.java
│           │               │   ├── SensorActivityTest.java
│           │               │   ├── SensorHeartTest.java
│           │               │   ├── SipTest.java
│           │               │   ├── SmsReadTest.java
│           │               │   ├── StandardChecker.java
│           │               │   ├── StorageReadTest.java
│           │               │   ├── StorageWriteTest.java
│           │               │   └── StrictChecker.java
│           │               ├── install/
│           │               │   ├── BaseRequest.java
│           │               │   ├── InstallRequest.java
│           │               │   ├── NRequest.java
│           │               │   ├── NRequestFactory.java
│           │               │   ├── ORequest.java
│           │               │   └── ORequestFactory.java
│           │               ├── notify/
│           │               │   ├── BaseRequest.java
│           │               │   ├── NRequest.java
│           │               │   ├── NRequestFactory.java
│           │               │   ├── Notify.java
│           │               │   ├── ORequest.java
│           │               │   ├── ORequestFactory.java
│           │               │   ├── PermissionRequest.java
│           │               │   ├── listener/
│           │               │   │   ├── BaseRequest.java
│           │               │   │   ├── J1Request.java
│           │               │   │   ├── J1RequestFactory.java
│           │               │   │   ├── J2Request.java
│           │               │   │   ├── J2RequestFactory.java
│           │               │   │   └── ListenerRequest.java
│           │               │   └── option/
│           │               │       └── NotifyOption.java
│           │               ├── option/
│           │               │   └── Option.java
│           │               ├── overlay/
│           │               │   ├── BaseRequest.java
│           │               │   ├── LRequest.java
│           │               │   ├── LRequestFactory.java
│           │               │   ├── MRequest.java
│           │               │   ├── MRequestFactory.java
│           │               │   ├── OverlayRequest.java
│           │               │   └── setting/
│           │               │       ├── LSettingPage.java
│           │               │       └── MSettingPage.java
│           │               ├── runtime/
│           │               │   ├── BaseRequest.java
│           │               │   ├── LRequest.java
│           │               │   ├── LRequestFactory.java
│           │               │   ├── MRequest.java
│           │               │   ├── MRequestFactory.java
│           │               │   ├── Permission.java
│           │               │   ├── PermissionDef.java
│           │               │   ├── PermissionRequest.java
│           │               │   ├── Runtime.java
│           │               │   ├── option/
│           │               │   │   └── RuntimeOption.java
│           │               │   └── setting/
│           │               │       ├── AllRequest.java
│           │               │       ├── SettingPage.java
│           │               │       └── SettingRequest.java
│           │               ├── setting/
│           │               │   ├── Setting.java
│           │               │   └── write/
│           │               │       ├── BaseRequest.java
│           │               │       ├── LWriteRequest.java
│           │               │       ├── LWriteRequestFactory.java
│           │               │       ├── MWriteRequest.java
│           │               │       ├── MWriteRequestFactory.java
│           │               │       └── WriteRequest.java
│           │               ├── source/
│           │               │   ├── ActivitySource.java
│           │               │   ├── ContextSource.java
│           │               │   ├── FragmentSource.java
│           │               │   ├── Source.java
│           │               │   ├── WrapperSource.java
│           │               │   └── XFragmentSource.java
│           │               ├── task/
│           │               │   ├── TaskExecutor.java
│           │               │   └── WaitDialog.java
│           │               └── util/
│           │                   └── StringUtils.java
│           └── res/
│               ├── drawable/
│               │   └── permission_shape_wait_background.xml
│               ├── layout/
│               │   └── permission_dialog_wait.xml
│               ├── values/
│               │   ├── dimens.xml
│               │   ├── string.xml
│               │   └── style.xml
│               ├── values-v21/
│               │   └── style.xml
│               ├── values-zh/
│               │   └── string.xml
│               ├── values-zh-RHK/
│               │   └── string.xml
│               ├── values-zh-RTW/
│               │   └── string.xml
│               └── xml/
│                   └── permission_file_paths.xml
├── sample/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── assets/
│           │   └── android.apk
│           ├── java/
│           │   └── com/
│           │       └── yanzhenjie/
│           │           └── permission/
│           │               └── sample/
│           │                   ├── App.java
│           │                   ├── InstallRationale.java
│           │                   ├── NotifyListenerRationale.java
│           │                   ├── NotifyRationale.java
│           │                   ├── OverlayRationale.java
│           │                   ├── RuntimeRationale.java
│           │                   ├── WriteSettingRationale.java
│           │                   ├── app/
│           │                   │   ├── MainActivity.java
│           │                   │   └── NotifyListenerService.java
│           │                   ├── util/
│           │                   │   ├── FileUtils.java
│           │                   │   └── IOUtils.java
│           │                   └── widget/
│           │                       ├── AlertWindow.java
│           │                       └── LauncherView.java
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   └── window_launcher.xml
│               └── values/
│                   ├── array_string.xml
│                   ├── colors.xml
│                   ├── dimens.xml
│                   ├── strings.xml
│                   └── styles.xml
└── settings.gradle
Download .txt
SYMBOL INDEX (600 symbols across 104 files)

FILE: permission/src/main/java/com/yanzhenjie/permission/Action.java
  type Action (line 21) | public interface Action<T> {
    method onAction (line 28) | void onAction(T data);

FILE: permission/src/main/java/com/yanzhenjie/permission/AndPermission.java
  class AndPermission (line 42) | public class AndPermission {
    method bridgeAction (line 46) | public static String bridgeAction(Context context, String suffix) {
    method with (line 56) | public static Option with(Context context) {
    method with (line 66) | public static Option with(Fragment fragment) {
    method with (line 76) | public static Option with(android.app.Fragment fragment) {
    method with (line 86) | public static Option with(Activity activity) {
    method hasAlwaysDeniedPermission (line 97) | public static boolean hasAlwaysDeniedPermission(Context context, List<...
    method hasAlwaysDeniedPermission (line 108) | public static boolean hasAlwaysDeniedPermission(Fragment fragment, Lis...
    method hasAlwaysDeniedPermission (line 119) | public static boolean hasAlwaysDeniedPermission(android.app.Fragment f...
    method hasAlwaysDeniedPermission (line 130) | public static boolean hasAlwaysDeniedPermission(Activity activity, Lis...
    method hasAlwaysDeniedPermission (line 137) | private static boolean hasAlwaysDeniedPermission(Source source, List<S...
    method hasAlwaysDeniedPermission (line 153) | public static boolean hasAlwaysDeniedPermission(Context context, Strin...
    method hasAlwaysDeniedPermission (line 164) | public static boolean hasAlwaysDeniedPermission(Fragment fragment, Str...
    method hasAlwaysDeniedPermission (line 175) | public static boolean hasAlwaysDeniedPermission(android.app.Fragment f...
    method hasAlwaysDeniedPermission (line 186) | public static boolean hasAlwaysDeniedPermission(Activity activity, Str...
    method hasAlwaysDeniedPermission (line 193) | private static boolean hasAlwaysDeniedPermission(Source source, String...
    method hasPermissions (line 214) | public static boolean hasPermissions(Context context, String... permis...
    method hasPermissions (line 225) | public static boolean hasPermissions(Fragment fragment, String... perm...
    method hasPermissions (line 236) | public static boolean hasPermissions(android.app.Fragment fragment, St...
    method hasPermissions (line 247) | public static boolean hasPermissions(Activity activity, String... perm...
    method hasPermissions (line 258) | public static boolean hasPermissions(Context context, String[]... perm...
    method hasPermissions (line 273) | public static boolean hasPermissions(Fragment fragment, String[]... pe...
    method hasPermissions (line 284) | public static boolean hasPermissions(android.app.Fragment fragment, St...
    method hasPermissions (line 295) | public static boolean hasPermissions(Activity activity, String[]... pe...
    method getFileUri (line 310) | public static Uri getFileUri(Context context, File file) {
    method getFileUri (line 324) | public static Uri getFileUri(Fragment fragment, File file) {
    method getFileUri (line 335) | public static Uri getFileUri(android.app.Fragment fragment, File file) {
    method getContextSource (line 339) | private static Source getContextSource(Context context) {
    method AndPermission (line 348) | private AndPermission() {

FILE: permission/src/main/java/com/yanzhenjie/permission/Boot.java
  class Boot (line 37) | public class Boot implements Option {
    type InstallRequestFactory (line 56) | public interface InstallRequestFactory {
      method create (line 61) | InstallRequest create(Source source);
    type OverlayRequestFactory (line 64) | public interface OverlayRequestFactory {
      method create (line 69) | OverlayRequest create(Source source);
    method Boot (line 74) | public Boot(Source source) {
    method runtime (line 78) | @Override
    method install (line 83) | @Override
    method overlay (line 88) | @Override
    method notification (line 93) | @Override
    method setting (line 98) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/FileProvider.java
  class FileProvider (line 49) | public class FileProvider extends ContentProvider {
    method onCreate (line 72) | @Override
    method attachInfo (line 77) | @Override
    method getUriForFile (line 90) | public static Uri getUriForFile(Context context, String authority, Fil...
    method query (line 95) | @Override
    method getType (line 123) | @Override
    method insert (line 138) | @Override
    method update (line 143) | @Override
    method delete (line 148) | @Override
    method openFile (line 154) | @Override
    method getPathStrategy (line 161) | private static PathStrategy getPathStrategy(Context context, String au...
    method parsePathStrategy (line 181) | private static PathStrategy parsePathStrategy(Context context, String ...
    type PathStrategy (line 234) | interface PathStrategy {
      method getUriForFile (line 236) | Uri getUriForFile(File file);
      method getFileForUri (line 238) | File getFileForUri(Uri uri);
    class SimplePathStrategy (line 241) | static class SimplePathStrategy implements PathStrategy {
      method SimplePathStrategy (line 246) | SimplePathStrategy(String authority) {
      method addRoot (line 250) | void addRoot(String name, File root) {
      method getUriForFile (line 264) | @Override
      method getFileForUri (line 298) | @Override
    method modeToMode (line 325) | private static int modeToMode(String mode) {
    method buildPath (line 346) | private static File buildPath(File base, String... segments) {
    method copyOf (line 356) | private static String[] copyOf(String[] original, int newLength) {
    method copyOf (line 362) | private static Object[] copyOf(Object[] original, int newLength) {
    method getExternalFilesDirs (line 368) | private static File[] getExternalFilesDirs(Context context, String typ...
    method getExternalCacheDirs (line 376) | public static File[] getExternalCacheDirs(Context context) {

FILE: permission/src/main/java/com/yanzhenjie/permission/Rationale.java
  type Rationale (line 23) | public interface Rationale<T> {
    method showRationale (line 32) | void showRationale(Context context, T data, RequestExecutor executor);

FILE: permission/src/main/java/com/yanzhenjie/permission/RequestExecutor.java
  type RequestExecutor (line 22) | public interface RequestExecutor {
    method execute (line 27) | void execute();
    method cancel (line 32) | void cancel();

FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeActivity.java
  class BridgeActivity (line 39) | public final class BridgeActivity extends Activity {
    method requestAppDetails (line 48) | static void requestAppDetails(Source source, String suffix) {
    method requestPermission (line 58) | static void requestPermission(Source source, String suffix, String[] p...
    method requestInstall (line 69) | static void requestInstall(Source source, String suffix) {
    method requestOverlay (line 79) | static void requestOverlay(Source source, String suffix) {
    method requestAlertWindow (line 89) | static void requestAlertWindow(Source source, String suffix) {
    method requestNotify (line 99) | static void requestNotify(Source source, String suffix) {
    method requestNotificationListener (line 109) | static void requestNotificationListener(Source source, String suffix) {
    method requestWriteSetting (line 119) | static void requestWriteSetting(Source source, String suffix) {
    method onCreate (line 128) | @Override
    method onRequestPermissionsResult (line 186) | @Override
    method startActivityForResult (line 192) | @Override
    method onActivityResult (line 197) | @Override
    method onKeyDown (line 204) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeRequest.java
  class BridgeRequest (line 25) | public final class BridgeRequest {
    method BridgeRequest (line 42) | public BridgeRequest(Source source) {
    method getSource (line 46) | public Source getSource() {
    method getType (line 50) | public int getType() {
    method setType (line 54) | public void setType(int type) {
    method getCallback (line 58) | public Callback getCallback() {
    method setCallback (line 62) | public void setCallback(Callback callback) {
    method getPermissions (line 66) | public List<String> getPermissions() {
    method setPermissions (line 70) | public void setPermissions(List<String> permissions) {
    type Callback (line 74) | public interface Callback {
      method onCallback (line 76) | void onCallback();

FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeService.java
  class BridgeService (line 31) | public class BridgeService extends Service {
    method onBind (line 33) | @Nullable
    method requestAppDetails (line 43) | @Override
    method requestPermission (line 48) | @Override
    method requestInstall (line 53) | @Override
    method requestOverlay (line 58) | @Override
    method requestAlertWindow (line 63) | @Override
    method requestNotify (line 68) | @Override
    method requestNotificationListener (line 73) | @Override
    method requestWriteSetting (line 78) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/Messenger.java
  class Messenger (line 28) | class Messenger extends BroadcastReceiver {
    method send (line 30) | public static void send(Context context, String suffix) {
    method Messenger (line 38) | public Messenger(Context context, Callback callback) {
    method register (line 43) | public void register(String suffix) {
    method unRegister (line 48) | public void unRegister() {
    method onReceive (line 52) | @Override
    type Callback (line 57) | public interface Callback {
      method onCallback (line 59) | void onCallback();

FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/RequestExecutor.java
  class RequestExecutor (line 33) | final class RequestExecutor extends Thread implements Messenger.Callback {
    method RequestExecutor (line 38) | public RequestExecutor(BridgeRequest request) {
    method run (line 42) | @Override
    method onServiceConnected (line 56) | @Override
    method onServiceDisconnected (line 66) | @Override
    method executeCurrent (line 71) | private void executeCurrent(IBridge iBridge) throws RemoteException {
    method onCallback (line 111) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/RequestManager.java
  class RequestManager (line 24) | public class RequestManager {
    method get (line 28) | public static RequestManager get() {
    method RequestManager (line 41) | private RequestManager() {
    method add (line 45) | public void add(BridgeRequest request) {

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CalendarReadTest.java
  class CalendarReadTest (line 26) | class CalendarReadTest implements PermissionTest {
    method CalendarReadTest (line 30) | CalendarReadTest(Context context) {
    method test (line 34) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CalendarWriteTest.java
  class CalendarWriteTest (line 33) | class CalendarWriteTest implements PermissionTest {
    method CalendarWriteTest (line 40) | CalendarWriteTest(Context context) {
    method test (line 44) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CallLogReadTest.java
  class CallLogReadTest (line 26) | class CallLogReadTest implements PermissionTest {
    method CallLogReadTest (line 30) | CallLogReadTest(Context context) {
    method test (line 34) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CallLogWriteTest.java
  class CallLogWriteTest (line 28) | class CallLogWriteTest implements PermissionTest {
    method CallLogWriteTest (line 32) | CallLogWriteTest(Context context) {
    method test (line 36) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CameraTest.java
  class CameraTest (line 25) | class CameraTest implements PermissionTest {
    method CameraTest (line 29) | CameraTest(Context context) {
    method test (line 33) | @Override
    method onPreviewFrame (line 59) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/ContactsReadTest.java
  class ContactsReadTest (line 26) | class ContactsReadTest implements PermissionTest {
    method ContactsReadTest (line 30) | ContactsReadTest(Context context) {
    method test (line 34) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/ContactsWriteTest.java
  class ContactsWriteTest (line 30) | class ContactsWriteTest implements PermissionTest {
    method ContactsWriteTest (line 36) | ContactsWriteTest(ContentResolver resolver) {
    method test (line 40) | @Override
    method insert (line 51) | private long[] insert() {
    method delete (line 65) | private boolean delete(long rawContactId, long dataId) {
    method update (line 73) | private boolean update(long rawContactId) {
    method query (line 83) | @Nullable

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/DoubleChecker.java
  class DoubleChecker (line 25) | public final class DoubleChecker implements PermissionChecker {
    method hasPermission (line 30) | @Override
    method hasPermission (line 36) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/LocationCoarseTest.java
  class LocationCoarseTest (line 27) | class LocationCoarseTest implements PermissionTest {
    method LocationCoarseTest (line 31) | LocationCoarseTest(Context context) {
    method test (line 35) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/LocationFineTest.java
  class LocationFineTest (line 27) | class LocationFineTest implements PermissionTest {
    method LocationFineTest (line 31) | LocationFineTest(Context context) {
    method test (line 35) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PermissionChecker.java
  type PermissionChecker (line 25) | public interface PermissionChecker {
    method hasPermission (line 35) | boolean hasPermission(Context context, String... permissions);
    method hasPermission (line 45) | boolean hasPermission(Context context, List<String> permissions);

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PermissionTest.java
  type PermissionTest (line 23) | interface PermissionTest {
    method test (line 25) | boolean test() throws Throwable;
    class CursorTest (line 27) | class CursorTest {
      method read (line 29) | public static void read(Cursor cursor) {

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PhoneStateReadTest.java
  class PhoneStateReadTest (line 26) | class PhoneStateReadTest implements PermissionTest {
    method PhoneStateReadTest (line 30) | PhoneStateReadTest(Context context) {
    method test (line 34) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/RecordAudioTest.java
  class RecordAudioTest (line 31) | class RecordAudioTest implements PermissionTest {
    method RecordAudioTest (line 37) | RecordAudioTest(Context context) {
    method test (line 41) | @Override
    method existMicrophone (line 91) | public static boolean existMicrophone(Context context) {
    method findAudioParameters (line 96) | public static int[] findAudioParameters() {

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SensorActivityTest.java
  class SensorActivityTest (line 28) | class SensorActivityTest implements PermissionTest {
    method SensorActivityTest (line 32) | SensorActivityTest(Context context) {
    method test (line 36) | @Override
    method onSensorChanged (line 51) | @Override
    method onAccuracyChanged (line 55) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SensorHeartTest.java
  class SensorHeartTest (line 28) | class SensorHeartTest implements PermissionTest {
    method SensorHeartTest (line 32) | SensorHeartTest(Context context) {
    method test (line 36) | @Override
    method onSensorChanged (line 51) | @Override
    method onAccuracyChanged (line 55) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SipTest.java
  class SipTest (line 27) | class SipTest implements PermissionTest {
    method SipTest (line 35) | SipTest(Context context) {
    method test (line 39) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SmsReadTest.java
  class SmsReadTest (line 26) | class SmsReadTest implements PermissionTest {
    method SmsReadTest (line 30) | SmsReadTest(Context context) {
    method test (line 34) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StandardChecker.java
  class StandardChecker (line 30) | public final class StandardChecker implements PermissionChecker {
    method StandardChecker (line 35) | public StandardChecker() {
    method hasPermission (line 38) | @Override
    method hasPermission (line 43) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StorageReadTest.java
  class StorageReadTest (line 27) | class StorageReadTest implements PermissionTest {
    method StorageReadTest (line 29) | StorageReadTest() {
    method test (line 32) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StorageWriteTest.java
  class StorageWriteTest (line 28) | class StorageWriteTest implements PermissionTest {
    method StorageWriteTest (line 32) | StorageWriteTest(Context c) {
    method test (line 36) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StrictChecker.java
  class StrictChecker (line 29) | public final class StrictChecker implements PermissionChecker {
    method StrictChecker (line 31) | public StrictChecker() {
    method hasPermission (line 34) | @Override
    method hasPermission (line 46) | @Override
    method hasPermission (line 58) | private boolean hasPermission(Context context, String permission) {
    method checkReadCalendar (line 116) | private static boolean checkReadCalendar(Context context) throws Throw...
    method checkWriteCalendar (line 121) | private static boolean checkWriteCalendar(Context context) throws Thro...
    method checkCamera (line 126) | private static boolean checkCamera(Context context) throws Throwable {
    method checkReadContacts (line 131) | private static boolean checkReadContacts(Context context) throws Throw...
    method checkWriteContacts (line 136) | private static boolean checkWriteContacts(Context context) throws Thro...
    method checkCoarseLocation (line 142) | private static boolean checkCoarseLocation(Context context) throws Thr...
    method checkFineLocation (line 147) | private static boolean checkFineLocation(Context context) throws Throw...
    method checkRecordAudio (line 152) | private static boolean checkRecordAudio(Context context) throws Throwa...
    method checkReadPhoneState (line 157) | private static boolean checkReadPhoneState(Context context) throws Thr...
    method checkReadCallLog (line 162) | private static boolean checkReadCallLog(Context context) throws Throwa...
    method checkWriteCallLog (line 167) | private static boolean checkWriteCallLog(Context context) throws Throw...
    method checkSip (line 172) | private static boolean checkSip(Context context) throws Throwable {
    method checkSensorHeart (line 177) | private static boolean checkSensorHeart(Context context) throws Throwa...
    method checkSensorActivity (line 182) | private static boolean checkSensorActivity(Context context) throws Thr...
    method checkReadSms (line 187) | private static boolean checkReadSms(Context context) throws Throwable {
    method checkReadStorage (line 192) | private static boolean checkReadStorage() throws Throwable {
    method checkWriteStorage (line 197) | private static boolean checkWriteStorage(Context context) throws Throw...

FILE: permission/src/main/java/com/yanzhenjie/permission/install/BaseRequest.java
  class BaseRequest (line 33) | abstract class BaseRequest implements InstallRequest {
    method showRationale (line 39) | @Override
    method BaseRequest (line 47) | BaseRequest(Source source) {
    method file (line 51) | @Override
    method rationale (line 57) | @Override
    method onGranted (line 63) | @Override
    method onDenied (line 69) | @Override
    method showRationale (line 78) | final void showRationale(RequestExecutor executor) {
    method install (line 85) | final void install() {
    method callbackSucceed (line 99) | final void callbackSucceed() {
    method callbackFailed (line 108) | final void callbackFailed() {

FILE: permission/src/main/java/com/yanzhenjie/permission/install/InstallRequest.java
  type InstallRequest (line 26) | public interface InstallRequest {
    method file (line 33) | InstallRequest file(File file);
    method rationale (line 38) | InstallRequest rationale(Rationale<File> rationale);
    method onGranted (line 43) | InstallRequest onGranted(Action<File> granted);
    method onDenied (line 48) | InstallRequest onDenied(Action<File> denied);
    method start (line 53) | void start();

FILE: permission/src/main/java/com/yanzhenjie/permission/install/NRequest.java
  class NRequest (line 23) | class NRequest extends BaseRequest {
    method NRequest (line 25) | NRequest(Source source) {
    method start (line 29) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/install/NRequestFactory.java
  class NRequestFactory (line 24) | public class NRequestFactory implements Boot.InstallRequestFactory {
    method create (line 26) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/install/ORequest.java
  class ORequest (line 26) | class ORequest extends BaseRequest implements RequestExecutor, BridgeReq...
    method ORequest (line 30) | ORequest(Source source) {
    method start (line 35) | @Override
    method execute (line 45) | @Override
    method cancel (line 53) | @Override
    method onCallback (line 58) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/install/ORequestFactory.java
  class ORequestFactory (line 24) | public class ORequestFactory implements Boot.InstallRequestFactory {
    method create (line 26) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/BaseRequest.java
  class BaseRequest (line 28) | abstract class BaseRequest implements PermissionRequest {
    method showRationale (line 33) | @Override
    method BaseRequest (line 41) | BaseRequest(Source source) {
    method rationale (line 45) | @Override
    method onGranted (line 51) | @Override
    method onDenied (line 57) | @Override
    method showRationale (line 66) | final void showRationale(RequestExecutor executor) {
    method callbackSucceed (line 73) | final void callbackSucceed() {
    method callbackFailed (line 82) | final void callbackFailed() {

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/NRequest.java
  class NRequest (line 26) | class NRequest extends BaseRequest implements RequestExecutor, BridgeReq...
    method NRequest (line 30) | NRequest(Source source) {
    method start (line 35) | @Override
    method execute (line 44) | @Override
    method cancel (line 52) | @Override
    method onCallback (line 57) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/NRequestFactory.java
  class NRequestFactory (line 23) | public class NRequestFactory implements Notify.PermissionRequestFactory {
    method create (line 25) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/Notify.java
  class Notify (line 29) | public class Notify implements NotifyOption {
    type PermissionRequestFactory (line 48) | public interface PermissionRequestFactory {
      method create (line 53) | PermissionRequest create(Source source);
    type ListenerRequestFactory (line 56) | public interface ListenerRequestFactory {
      method create (line 61) | ListenerRequest create(Source source);
    method Notify (line 66) | public Notify(Source source) {
    method permission (line 70) | public PermissionRequest permission() {
    method listener (line 74) | public ListenerRequest listener() {

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/ORequest.java
  class ORequest (line 26) | class ORequest extends BaseRequest implements RequestExecutor, BridgeReq...
    method ORequest (line 30) | ORequest(Source source) {
    method start (line 35) | @Override
    method execute (line 44) | @Override
    method cancel (line 52) | @Override
    method onCallback (line 57) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/ORequestFactory.java
  class ORequestFactory (line 23) | public class ORequestFactory implements Notify.PermissionRequestFactory {
    method create (line 25) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/PermissionRequest.java
  type PermissionRequest (line 24) | public interface PermissionRequest {
    method rationale (line 29) | PermissionRequest rationale(Rationale<Void> rationale);
    method onGranted (line 34) | PermissionRequest onGranted(Action<Void> granted);
    method onDenied (line 39) | PermissionRequest onDenied(Action<Void> denied);
    method start (line 44) | void start();

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/BaseRequest.java
  class BaseRequest (line 28) | abstract class BaseRequest implements ListenerRequest {
    method showRationale (line 33) | @Override
    method BaseRequest (line 41) | BaseRequest(Source source) {
    method rationale (line 45) | @Override
    method onGranted (line 51) | @Override
    method onDenied (line 57) | @Override
    method showRationale (line 66) | final void showRationale(RequestExecutor executor) {
    method callbackSucceed (line 73) | final void callbackSucceed() {
    method callbackFailed (line 82) | final void callbackFailed() {

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J1Request.java
  class J1Request (line 24) | class J1Request extends BaseRequest implements RequestExecutor {
    method J1Request (line 26) | J1Request(Source source) {
    method start (line 30) | @Override
    method execute (line 35) | @Override
    method cancel (line 40) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J1RequestFactory.java
  class J1RequestFactory (line 24) | public class J1RequestFactory implements Notify.ListenerRequestFactory {
    method create (line 26) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J2Request.java
  class J2Request (line 26) | class J2Request extends BaseRequest implements RequestExecutor, BridgeRe...
    method J2Request (line 30) | J2Request(Source source) {
    method start (line 35) | @Override
    method execute (line 44) | @Override
    method cancel (line 52) | @Override
    method onCallback (line 57) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J2RequestFactory.java
  class J2RequestFactory (line 24) | public class J2RequestFactory implements Notify.ListenerRequestFactory {
    method create (line 26) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/ListenerRequest.java
  type ListenerRequest (line 24) | public interface ListenerRequest {
    method rationale (line 29) | ListenerRequest rationale(Rationale<Void> rationale);
    method onGranted (line 34) | ListenerRequest onGranted(Action<Void> granted);
    method onDenied (line 39) | ListenerRequest onDenied(Action<Void> denied);
    method start (line 44) | void start();

FILE: permission/src/main/java/com/yanzhenjie/permission/notify/option/NotifyOption.java
  type NotifyOption (line 24) | public interface NotifyOption {
    method permission (line 29) | PermissionRequest permission();
    method listener (line 34) | ListenerRequest listener();

FILE: permission/src/main/java/com/yanzhenjie/permission/option/Option.java
  type Option (line 27) | public interface Option {
    method runtime (line 32) | RuntimeOption runtime();
    method install (line 37) | InstallRequest install();
    method overlay (line 42) | OverlayRequest overlay();
    method notification (line 47) | NotifyOption notification();
    method setting (line 52) | Setting setting();

FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/BaseRequest.java
  class BaseRequest (line 33) | abstract class BaseRequest implements OverlayRequest {
    method showRationale (line 38) | @Override
    method BaseRequest (line 46) | BaseRequest(Source source) {
    method rationale (line 50) | @Override
    method onGranted (line 56) | @Override
    method onDenied (line 62) | @Override
    method showRationale (line 71) | final void showRationale(RequestExecutor executor) {
    method callbackSucceed (line 78) | final void callbackSucceed() {
    method callbackFailed (line 87) | final void callbackFailed() {
    method tryDisplayDialog (line 93) | static boolean tryDisplayDialog(Context context) {

FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/LRequest.java
  class LRequest (line 26) | class LRequest extends BaseRequest implements RequestExecutor, BridgeReq...
    method LRequest (line 30) | LRequest(Source source) {
    method start (line 35) | @Override
    method execute (line 44) | @Override
    method cancel (line 52) | @Override
    method onCallback (line 57) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/LRequestFactory.java
  class LRequestFactory (line 24) | public class LRequestFactory implements Boot.OverlayRequestFactory {
    method create (line 26) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/MRequest.java
  class MRequest (line 26) | class MRequest extends BaseRequest implements RequestExecutor, BridgeReq...
    method MRequest (line 30) | MRequest(Source source) {
    method start (line 35) | @Override
    method execute (line 44) | @Override
    method cancel (line 52) | @Override
    method onCallback (line 57) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/MRequestFactory.java
  class MRequestFactory (line 24) | public class MRequestFactory implements Boot.OverlayRequestFactory {
    method create (line 26) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/OverlayRequest.java
  type OverlayRequest (line 24) | public interface OverlayRequest {
    method rationale (line 29) | OverlayRequest rationale(Rationale<Void> rationale);
    method onGranted (line 34) | OverlayRequest onGranted(Action<Void> granted);
    method onDenied (line 39) | OverlayRequest onDenied(Action<Void> denied);
    method start (line 44) | void start();

FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/setting/LSettingPage.java
  class LSettingPage (line 31) | public class LSettingPage {
    method LSettingPage (line 37) | public LSettingPage(Source source) {
    method start (line 41) | public void start(int requestCode) {
    method defaultApi (line 65) | private static Intent defaultApi(Context context) {
    method huaweiApi (line 71) | private Intent huaweiApi(Context context) {
    method xiaomiApi (line 88) | private Intent xiaomiApi(Context context) {
    method oppoApi (line 99) | private Intent oppoApi(Context context) {
    method vivoApi (line 115) | private Intent vivoApi(Context context) {
    method meizuApi (line 127) | private Intent meizuApi(Context context) {
    method hasActivity (line 136) | private static boolean hasActivity(Context context, Intent intent) {

FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/setting/MSettingPage.java
  class MSettingPage (line 30) | public class MSettingPage {
    method MSettingPage (line 36) | public MSettingPage(Source source) {
    method start (line 40) | public void start(int requestCode) {
    method appDetailsApi (line 56) | private static Intent appDetailsApi(Context context) {
    method defaultApi (line 62) | private static Intent defaultApi(Context context) {
    method meiZuApi (line 70) | private static Intent meiZuApi(Context context) {
    method hasActivity (line 79) | private static boolean hasActivity(Context context, Intent intent) {

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/BaseRequest.java
  class BaseRequest (line 36) | abstract class BaseRequest implements PermissionRequest {
    method showRationale (line 41) | @Override
    method BaseRequest (line 49) | BaseRequest(Source source) {
    method rationale (line 53) | @Override
    method onGranted (line 59) | @Override
    method onDenied (line 65) | @Override
    method showRationale (line 74) | final void showRationale(List<String> rationaleList, RequestExecutor e...
    method callbackSucceed (line 81) | final void callbackSucceed(List<String> grantedList) {
    method callbackFailed (line 90) | final void callbackFailed(List<String> deniedList) {
    method filterPermissions (line 99) | public static List<String> filterPermissions(List<String> permissions) {
    method getDeniedPermissions (line 116) | public static List<String> getDeniedPermissions(PermissionChecker chec...
    method getRationalePermissions (line 129) | public static List<String> getRationalePermissions(Source source, List...

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/LRequest.java
  class LRequest (line 32) | class LRequest extends BaseRequest {
    method LRequest (line 40) | LRequest(Source source) {
    method permission (line 45) | @Override
    method permission (line 52) | @Override
    method start (line 61) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/LRequestFactory.java
  class LRequestFactory (line 23) | public class LRequestFactory implements Runtime.PermissionRequestFactory {
    method create (line 25) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/MRequest.java
  class MRequest (line 36) | class MRequest extends BaseRequest implements RequestExecutor, BridgeReq...
    method MRequest (line 47) | MRequest(Source source) {
    method permission (line 52) | @Override
    method permission (line 59) | @Override
    method start (line 68) | @Override
    method execute (line 85) | @Override
    method cancel (line 94) | @Override
    method onCallback (line 99) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/MRequestFactory.java
  class MRequestFactory (line 23) | public class MRequestFactory implements Runtime.PermissionRequestFactory {
    method create (line 25) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/Permission.java
  class Permission (line 31) | public class Permission {
    class Group (line 71) | public static final class Group {
    method transformText (line 103) | public static List<String> transformText(Context context, String... pe...
    method transformText (line 110) | public static List<String> transformText(Context context, String[]... ...
    method transformText (line 121) | public static List<String> transformText(Context context, List<String>...

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/PermissionRequest.java
  type PermissionRequest (line 29) | public interface PermissionRequest {
    method permission (line 34) | PermissionRequest permission(@NonNull @PermissionDef String... permiss...
    method permission (line 41) | PermissionRequest permission(@NonNull String[]... groups);
    method rationale (line 46) | PermissionRequest rationale(@NonNull Rationale<List<String>> rationale);
    method onGranted (line 51) | PermissionRequest onGranted(@NonNull Action<List<String>> granted);
    method onDenied (line 56) | PermissionRequest onDenied(@NonNull Action<List<String>> denied);
    method start (line 61) | void start();

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/Runtime.java
  class Runtime (line 37) | public class Runtime implements RuntimeOption {
    type PermissionRequestFactory (line 52) | public interface PermissionRequestFactory {
      method create (line 57) | PermissionRequest create(Source source);
    method Runtime (line 62) | public Runtime(Source source) {
    method permission (line 66) | @Override
    method permission (line 72) | @Override
    method setting (line 83) | @Override
    method checkPermissions (line 94) | private void checkPermissions(String... permissions) {
    method getManifestPermissions (line 116) | public static List<String> getManifestPermissions(Context context) {

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/option/RuntimeOption.java
  type RuntimeOption (line 28) | public interface RuntimeOption {
    method permission (line 33) | PermissionRequest permission(@NonNull @PermissionDef String... permiss...
    method permission (line 40) | PermissionRequest permission(@NonNull String[]... groups);
    method setting (line 45) | SettingRequest setting();

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/setting/AllRequest.java
  class AllRequest (line 24) | public class AllRequest implements SettingRequest {
    method AllRequest (line 28) | public AllRequest(Source source) {
    method start (line 32) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/setting/SettingPage.java
  class SettingPage (line 30) | public class SettingPage {
    method SettingPage (line 36) | public SettingPage(Source source) {
    method start (line 45) | public void start(int requestCode) {
    method defaultApi (line 68) | private static Intent defaultApi(Context context) {
    method huaweiApi (line 74) | private static Intent huaweiApi(Context context) {
    method xiaomiApi (line 82) | private static Intent xiaomiApi(Context context) {
    method vivoApi (line 96) | private static Intent vivoApi(Context context) {
    method oppoApi (line 108) | private static Intent oppoApi(Context context) {
    method meizuApi (line 120) | private static Intent meizuApi(Context context) {
    method hasActivity (line 129) | private static boolean hasActivity(Context context, Intent intent) {

FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/setting/SettingRequest.java
  type SettingRequest (line 21) | public interface SettingRequest {
    method start (line 26) | void start(int requestCode);

FILE: permission/src/main/java/com/yanzhenjie/permission/setting/Setting.java
  class Setting (line 28) | public class Setting {
    type SettingRequestFactory (line 40) | public interface SettingRequestFactory {
      method create (line 42) | WriteRequest create(Source source);
    method Setting (line 47) | public Setting(Source source) {
    method write (line 54) | public WriteRequest write() {

FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/BaseRequest.java
  class BaseRequest (line 28) | abstract class BaseRequest implements WriteRequest {
    method showRationale (line 33) | @Override
    method BaseRequest (line 41) | BaseRequest(Source source) {
    method rationale (line 45) | @Override
    method onGranted (line 51) | @Override
    method onDenied (line 57) | @Override
    method showRationale (line 66) | final void showRationale(RequestExecutor executor) {
    method callbackSucceed (line 73) | final void callbackSucceed() {
    method callbackFailed (line 82) | final void callbackFailed() {

FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/LWriteRequest.java
  class LWriteRequest (line 23) | public class LWriteRequest extends BaseRequest {
    method LWriteRequest (line 25) | public LWriteRequest(Source source) {
    method start (line 29) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/LWriteRequestFactory.java
  class LWriteRequestFactory (line 24) | public class LWriteRequestFactory implements Setting.SettingRequestFacto...
    method create (line 26) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/MWriteRequest.java
  class MWriteRequest (line 26) | public class MWriteRequest extends BaseRequest implements RequestExecuto...
    method MWriteRequest (line 30) | public MWriteRequest(Source source) {
    method start (line 35) | @Override
    method execute (line 44) | @Override
    method cancel (line 52) | @Override
    method onCallback (line 57) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/MWriteRequestFactory.java
  class MWriteRequestFactory (line 24) | public class MWriteRequestFactory implements Setting.SettingRequestFacto...
    method create (line 26) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/WriteRequest.java
  type WriteRequest (line 24) | public interface WriteRequest {
    method rationale (line 29) | WriteRequest rationale(Rationale<Void> rationale);
    method onGranted (line 34) | WriteRequest onGranted(Action<Void> granted);
    method onDenied (line 39) | WriteRequest onDenied(Action<Void> denied);
    method start (line 44) | void start();

FILE: permission/src/main/java/com/yanzhenjie/permission/source/ActivitySource.java
  class ActivitySource (line 27) | public class ActivitySource extends Source {
    method ActivitySource (line 31) | public ActivitySource(Activity activity) {
    method getContext (line 35) | @Override
    method startActivity (line 40) | @Override
    method startActivityForResult (line 45) | @Override
    method isShowRationalePermission (line 50) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/source/ContextSource.java
  class ContextSource (line 29) | public class ContextSource extends Source {
    method ContextSource (line 33) | public ContextSource(Context context) {
    method getContext (line 37) | @Override
    method startActivity (line 42) | @Override
    method startActivityForResult (line 48) | @Override
    method isShowRationalePermission (line 53) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/source/FragmentSource.java
  class FragmentSource (line 27) | public class FragmentSource extends Source {
    method FragmentSource (line 31) | public FragmentSource(Fragment fragment) {
    method getContext (line 35) | @Override
    method startActivity (line 40) | @Override
    method startActivityForResult (line 45) | @Override
    method isShowRationalePermission (line 50) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/source/Source.java
  class Source (line 35) | public abstract class Source {
    method getContext (line 53) | public abstract Context getContext();
    method startActivity (line 55) | public abstract void startActivity(Intent intent);
    method startActivityForResult (line 57) | public abstract void startActivityForResult(Intent intent, int request...
    method isShowRationalePermission (line 59) | public abstract boolean isShowRationalePermission(String permission);
    method getTargetSdkVersion (line 61) | public int getTargetSdkVersion() {
    method getPackageName (line 68) | public String getPackageName() {
    method getPackageManager (line 75) | private PackageManager getPackageManager() {
    method getAppOpsManager (line 82) | @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    method getNotificationManager (line 90) | private NotificationManager getNotificationManager() {
    method canRequestPackageInstalls (line 97) | public final boolean canRequestPackageInstalls() {
    method canDrawOverlays (line 111) | public final boolean canDrawOverlays() {
    method canNotify (line 123) | public final boolean canNotify() {
    method canListenerNotification (line 133) | public final boolean canListenerNotification() {
    method canWriteSetting (line 143) | public final boolean canWriteSetting() {
    method reflectionOps (line 155) | @RequiresApi(api = Build.VERSION_CODES.KITKAT)

FILE: permission/src/main/java/com/yanzhenjie/permission/source/WrapperSource.java
  class WrapperSource (line 24) | public class WrapperSource extends Source {
    method WrapperSource (line 28) | public WrapperSource(Source source) {
    method getContext (line 32) | @Override
    method startActivity (line 37) | @Override
    method startActivityForResult (line 42) | @Override
    method isShowRationalePermission (line 47) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/source/XFragmentSource.java
  class XFragmentSource (line 28) | public class XFragmentSource extends Source {
    method XFragmentSource (line 32) | public XFragmentSource(Fragment fragment) {
    method getContext (line 36) | @Override
    method startActivity (line 41) | @Override
    method startActivityForResult (line 46) | @Override
    method isShowRationalePermission (line 51) | @Override

FILE: permission/src/main/java/com/yanzhenjie/permission/task/TaskExecutor.java
  class TaskExecutor (line 28) | public abstract class TaskExecutor<T> extends AsyncTask<Void, Void, T> {
    method TaskExecutor (line 34) | public TaskExecutor(Context context) {
    method onPreExecute (line 39) | @Override
    method onPostExecute (line 46) | @Override
    method onFinish (line 54) | protected abstract void onFinish(T t);
    method execute (line 59) | public final void execute() {

FILE: permission/src/main/java/com/yanzhenjie/permission/task/WaitDialog.java
  class WaitDialog (line 28) | public class WaitDialog extends AppCompatDialog {
    method WaitDialog (line 30) | public WaitDialog(@NonNull Context context) {

FILE: permission/src/main/java/com/yanzhenjie/permission/util/StringUtils.java
  class StringUtils (line 21) | public class StringUtils {
    method textToHex (line 26) | public static String textToHex(String text) {
    method hexToText (line 40) | public static String hexToText(String hexText) {

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/App.java
  class App (line 27) | public class App extends Application {
    method onCreate (line 31) | @Override
    method get (line 37) | public static App get() {
    method showLauncherView (line 41) | public void showLauncherView() {

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/InstallRationale.java
  class InstallRationale (line 30) | public class InstallRationale implements Rationale<File> {
    method showRationale (line 32) | @Override

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/NotifyListenerRationale.java
  class NotifyListenerRationale (line 28) | public class NotifyListenerRationale implements Rationale<Void> {
    method showRationale (line 30) | @Override

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/NotifyRationale.java
  class NotifyRationale (line 28) | public class NotifyRationale implements Rationale<Void> {
    method showRationale (line 30) | @Override

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/OverlayRationale.java
  class OverlayRationale (line 28) | public class OverlayRationale implements Rationale<Void> {
    method showRationale (line 30) | @Override

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/RuntimeRationale.java
  class RuntimeRationale (line 32) | public final class RuntimeRationale implements Rationale<List<String>> {
    method showRationale (line 34) | @Override

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/WriteSettingRationale.java
  class WriteSettingRationale (line 28) | public class WriteSettingRationale implements Rationale<Void> {
    method showRationale (line 30) | @Override

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/app/MainActivity.java
  class MainActivity (line 64) | public class MainActivity extends AppCompatActivity implements View.OnCl...
    method onCreate (line 68) | @Override
    method onClick (line 96) | @Override
    method requestPermission (line 365) | private void requestPermission(@PermissionDef String... permissions) {
    method showSettingDialog (line 391) | public void showSettingDialog(Context context, final List<String> perm...
    method setPermission (line 416) | private void setPermission() {
    method onActivityResult (line 420) | @Override
    method requestNotification (line 434) | private void requestNotification() {
    method requestNotificationListener (line 457) | private void requestNotificationListener() {
    method requestPermissionForInstallPackage (line 480) | private void requestPermissionForInstallPackage() {
    method writeApkForInstallPackage (line 514) | private void writeApkForInstallPackage() {
    method installPackage (line 558) | private void installPackage(File apkFile) {
    method requestPermissionForAlertWindow (line 578) | private void requestPermissionForAlertWindow() {
    method requestWriteSystemSetting (line 592) | private void requestWriteSystemSetting() {
    method showAlertWindow (line 606) | private void showAlertWindow() {
    method createMenu (line 618) | private PopupMenu createMenu(View v, String[] menuArray) {
    method toast (line 628) | protected void toast(@StringRes int message) {

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/app/NotifyListenerService.java
  class NotifyListenerService (line 27) | @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    method onNotificationPosted (line 30) | @Override
    method onNotificationRemoved (line 34) | @Override

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/util/FileUtils.java
  class FileUtils (line 29) | public class FileUtils {
    method getFileDir (line 31) | public static File getFileDir(Context context) {
    method getFileDir (line 35) | public static File getFileDir(Context context, @Nullable String type) {
    method externalAvailable (line 46) | public static boolean externalAvailable() {
    method getExternalDir (line 50) | public static File getExternalDir(Context context) {
    method getExternalDir (line 54) | public static File getExternalDir(Context context, @Nullable String ty...
    method getRootDir (line 72) | public static File getRootDir() {
    method getRootDir (line 76) | public static File getRootDir(@Nullable String type) {
    method createDir (line 94) | public static void createDir(File dir) {

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/util/IOUtils.java
  class IOUtils (line 27) | public class IOUtils {
    method write (line 29) | public static void write(InputStream in, OutputStream out) throws IOEx...
    method close (line 38) | public static void close(Closeable closeable) {
    method flush (line 46) | public static void flush(Flushable flushable) {

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/widget/AlertWindow.java
  class AlertWindow (line 31) | public class AlertWindow {
    method AlertWindow (line 40) | public AlertWindow(Context context) {
    method create (line 45) | private void create() {
    method setContentView (line 69) | public void setContentView(int layoutId) {
    method setContentView (line 78) | public void setContentView(View view) {
    method isShowing (line 95) | public boolean isShowing() {
    method show (line 102) | public void show() {
    method dismiss (line 114) | public void dismiss() {

FILE: sample/src/main/java/com/yanzhenjie/permission/sample/widget/LauncherView.java
  class LauncherView (line 28) | public class LauncherView extends RelativeLayout implements View.OnClick...
    method LauncherView (line 32) | public LauncherView(Context context) {
    method LauncherView (line 36) | public LauncherView(Context context, AttributeSet attrs) {
    method LauncherView (line 40) | public LauncherView(Context context, AttributeSet attrs, int defStyleA...
    method onClick (line 46) | @Override
    method setCancelClickListener (line 59) | public void setCancelClickListener(OnClickListener cancelClickListener) {
Condensed preview — 143 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (356K chars).
[
  {
    "path": ".gitignore",
    "chars": 88,
    "preview": "*.iml\n/.idea/\n/build/\n.gradle\n/local.properties\n.DS_Store\n/captures\n.externalNativeBuild"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 774,
    "preview": "# Contributing to AndPermission\nFirst off, thanks for taking the time to contribute.  \n\nThe following is a set of guidel"
  },
  {
    "path": "LICENSE",
    "chars": 11342,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 1715,
    "preview": "# AndPermission\n1. Request for runtime permissions.  \n2. Share private files.  \n3. Request to install unknown source ap"
  },
  {
    "path": "build.gradle",
    "chars": 607,
    "preview": "apply from: \"config.gradle\"\n\nbuildscript {\n    repositories {\n        google()\n        jcenter {url 'https://maven.aliyu"
  },
  {
    "path": "config.gradle",
    "chars": 1502,
    "preview": "ext {\n    plugins = [application: 'com.android.application',\n               library    : 'com.android.library',\n        "
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 201,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dist"
  },
  {
    "path": "gradle.properties",
    "chars": 25,
    "preview": "android.useAndroidX=true\n"
  },
  {
    "path": "gradlew",
    "chars": 4971,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "gradlew.bat",
    "chars": 2314,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "permission/.gitignore",
    "chars": 6,
    "preview": "/build"
  },
  {
    "path": "permission/build.gradle",
    "chars": 436,
    "preview": "apply plugin: rootProject.ext.plugins.library\n\nandroid {\n    compileSdkVersion rootProject.ext.android.compileSdkVersion"
  },
  {
    "path": "permission/src/main/AndroidManifest.xml",
    "chars": 2171,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/aidl/com/yanzhenjie/permission/bridge/IBridge.aidl",
    "chars": 851,
    "preview": "package com.yanzhenjie.permission.bridge;\n\ninterface IBridge {\n    /**\n     * Request for permissions.\n     */\n    void "
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/Action.java",
    "chars": 807,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/AndPermission.java",
    "chars": 12017,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/Boot.java",
    "chars": 2982,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/FileProvider.java",
    "chars": 14381,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/Rationale.java",
    "chars": 966,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/RequestExecutor.java",
    "chars": 874,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeActivity.java",
    "chars": 8226,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeRequest.java",
    "chars": 1985,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeService.java",
    "chars": 2645,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/bridge/Messenger.java",
    "chars": 1783,
    "preview": "/*\n * Copyright © 2018 Zhenjie Yan.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/bridge/RequestExecutor.java",
    "chars": 3898,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/bridge/RequestManager.java",
    "chars": 1373,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/CalendarReadTest.java",
    "chars": 1545,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/CalendarWriteTest.java",
    "chars": 3212,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/CallLogReadTest.java",
    "chars": 1517,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/CallLogWriteTest.java",
    "chars": 1727,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/CameraTest.java",
    "chars": 2029,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/ContactsReadTest.java",
    "chars": 1560,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/ContactsWriteTest.java",
    "chars": 4065,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/DoubleChecker.java",
    "chars": 1439,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/LocationCoarseTest.java",
    "chars": 1701,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/LocationFineTest.java",
    "chars": 1776,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/PermissionChecker.java",
    "chars": 1367,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/PermissionTest.java",
    "chars": 1529,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/PhoneStateReadTest.java",
    "chars": 1512,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/RecordAudioTest.java",
    "chars": 3831,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/SensorActivityTest.java",
    "chars": 2057,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/SensorHeartTest.java",
    "chars": 2044,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/SipTest.java",
    "chars": 1777,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/SmsReadTest.java",
    "chars": 1546,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/StandardChecker.java",
    "chars": 2290,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/StorageReadTest.java",
    "chars": 1430,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/StorageWriteTest.java",
    "chars": 1823,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/checker/StrictChecker.java",
    "chars": 7233,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/install/BaseRequest.java",
    "chars": 3099,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/install/InstallRequest.java",
    "chars": 1357,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/install/NRequest.java",
    "chars": 928,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/install/NRequestFactory.java",
    "chars": 954,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/install/ORequest.java",
    "chars": 1847,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/install/ORequestFactory.java",
    "chars": 954,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/BaseRequest.java",
    "chars": 2318,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/NRequest.java",
    "chars": 1772,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/NRequestFactory.java",
    "chars": 922,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/Notify.java",
    "chars": 2340,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/ORequest.java",
    "chars": 1767,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/ORequestFactory.java",
    "chars": 922,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/PermissionRequest.java",
    "chars": 1235,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/listener/BaseRequest.java",
    "chars": 2319,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/listener/J1Request.java",
    "chars": 1133,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/listener/J1RequestFactory.java",
    "chars": 977,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/listener/J2Request.java",
    "chars": 1815,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/listener/J2RequestFactory.java",
    "chars": 977,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/listener/ListenerRequest.java",
    "chars": 1236,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/notify/option/NotifyOption.java",
    "chars": 1011,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/option/Option.java",
    "chars": 1419,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/overlay/BaseRequest.java",
    "chars": 3102,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/overlay/LRequest.java",
    "chars": 1812,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/overlay/LRequestFactory.java",
    "chars": 954,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/overlay/MRequest.java",
    "chars": 1818,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/overlay/MRequestFactory.java",
    "chars": 954,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/overlay/OverlayRequest.java",
    "chars": 1225,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/overlay/setting/LSettingPage.java",
    "chars": 5539,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/overlay/setting/MSettingPage.java",
    "chars": 2849,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/BaseRequest.java",
    "chars": 4277,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/LRequest.java",
    "chars": 2432,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/LRequestFactory.java",
    "chars": 924,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/MRequest.java",
    "chars": 3692,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/MRequestFactory.java",
    "chars": 924,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/Permission.java",
    "chars": 10160,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/PermissionDef.java",
    "chars": 1784,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/PermissionRequest.java",
    "chars": 1699,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/Runtime.java",
    "chars": 4408,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/option/RuntimeOption.java",
    "chars": 1393,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/setting/AllRequest.java",
    "chars": 1091,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/setting/SettingPage.java",
    "chars": 5077,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/runtime/setting/SettingRequest.java",
    "chars": 805,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/setting/Setting.java",
    "chars": 1647,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/setting/write/BaseRequest.java",
    "chars": 2305,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/setting/write/LWriteRequest.java",
    "chars": 936,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/setting/write/LWriteRequestFactory.java",
    "chars": 979,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/setting/write/MWriteRequest.java",
    "chars": 1814,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/setting/write/MWriteRequestFactory.java",
    "chars": 979,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/setting/write/WriteRequest.java",
    "chars": 1220,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/source/ActivitySource.java",
    "chars": 1580,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/source/ContextSource.java",
    "chars": 2076,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/source/FragmentSource.java",
    "chars": 1605,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/source/Source.java",
    "chars": 6120,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/source/WrapperSource.java",
    "chars": 1406,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/source/XFragmentSource.java",
    "chars": 1628,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/task/TaskExecutor.java",
    "chars": 1645,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/task/WaitDialog.java",
    "chars": 1067,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/java/com/yanzhenjie/permission/util/StringUtils.java",
    "chars": 1798,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "permission/src/main/res/drawable/permission_shape_wait_background.xml",
    "chars": 835,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2019 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/layout/permission_dialog_wait.xml",
    "chars": 1630,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/values/dimens.xml",
    "chars": 1335,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/values/string.xml",
    "chars": 1439,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/values/style.xml",
    "chars": 2777,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2019 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/values-v21/style.xml",
    "chars": 1415,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2019 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/values-zh/string.xml",
    "chars": 1359,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/values-zh-RHK/string.xml",
    "chars": 1359,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/values-zh-RTW/string.xml",
    "chars": 1359,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "permission/src/main/res/xml/permission_file_paths.xml",
    "chars": 1151,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n    Copyright 2018 Zhenjie Yan\n\n    Licensed under the Apache License, Versio"
  },
  {
    "path": "sample/.gitignore",
    "chars": 6,
    "preview": "/build"
  },
  {
    "path": "sample/build.gradle",
    "chars": 633,
    "preview": "apply plugin: rootProject.ext.plugins.application\n\nandroid {\n    compileSdkVersion rootProject.ext.android.compileSdkVer"
  },
  {
    "path": "sample/proguard-rules.pro",
    "chars": 1866,
    "preview": "-optimizationpasses 5\n-ignorewarnings\n-dontusemixedcaseclassnames\n-dontskipnonpubliclibraryclasses\n-dontskipnonpubliclib"
  },
  {
    "path": "sample/src/main/AndroidManifest.xml",
    "chars": 4183,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright © Zhenjie Yan\n\n    Licensed under the Apache License, Version"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/App.java",
    "chars": 1531,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/InstallRationale.java",
    "chars": 1787,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/NotifyListenerRationale.java",
    "chars": 1789,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/NotifyRationale.java",
    "chars": 1772,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/OverlayRationale.java",
    "chars": 1765,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/RuntimeRationale.java",
    "chars": 2095,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/WriteSettingRationale.java",
    "chars": 1773,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/app/MainActivity.java",
    "chars": 24516,
    "preview": "/*\n * Copyright © Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use t"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/app/NotifyListenerService.java",
    "chars": 1184,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/util/FileUtils.java",
    "chars": 2988,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/util/IOUtils.java",
    "chars": 1485,
    "preview": "/*\n * Copyright 2019 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/widget/AlertWindow.java",
    "chars": 3729,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/java/com/yanzhenjie/permission/sample/widget/LauncherView.java",
    "chars": 1902,
    "preview": "/*\n * Copyright 2018 Zhenjie Yan\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "sample/src/main/res/layout/activity_main.xml",
    "chars": 5110,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright © Zhenjie Yan\n\n    Licensed under the Apache License, Version"
  },
  {
    "path": "sample/src/main/res/layout/window_launcher.xml",
    "chars": 1481,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Versi"
  },
  {
    "path": "sample/src/main/res/values/array_string.xml",
    "chars": 2049,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright 2017 Zhenjie Yan\n\n    Licensed under the Apache License, Vers"
  },
  {
    "path": "sample/src/main/res/values/colors.xml",
    "chars": 843,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    Copyright © Zhenjie Yan\n\n    Licensed under the Apache License, Version "
  },
  {
    "path": "sample/src/main/res/values/dimens.xml",
    "chars": 1035,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    Copyright © Zhenjie Yan\n\n    Licensed under the Apache License, Version "
  },
  {
    "path": "sample/src/main/res/values/strings.xml",
    "chars": 3261,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n    Copyright © Zhenjie Yan\n\n    Licensed under the Apache License, Version"
  },
  {
    "path": "sample/src/main/res/values/styles.xml",
    "chars": 2811,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n    Copyright © Zhenjie Yan\n\n    Licensed under the Apache License, Version "
  },
  {
    "path": "settings.gradle",
    "chars": 32,
    "preview": "include ':permission', ':sample'"
  }
]

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

About this extraction

This page contains the full source code of the yanzhenjie/AndPermission GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 143 files (323.5 KB), approximately 74.3k tokens, and a symbol index with 600 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!