├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── ci-gradle.properties
├── pull_request_template.md
└── workflows
│ ├── check.yml
│ ├── release.yml
│ └── scheduled.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── README_ZH.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── qifan
│ │ │ └── powerpermission
│ │ │ ├── MainActivity.kt
│ │ │ ├── activity
│ │ │ └── ExampleActivity.kt
│ │ │ ├── childfragment
│ │ │ ├── ExampleChildContainerFragment.kt
│ │ │ └── ExampleChildFragment.kt
│ │ │ ├── coroutines
│ │ │ └── CoroutinesActivity.kt
│ │ │ ├── fragment
│ │ │ └── ExampleFragment.kt
│ │ │ ├── livedata
│ │ │ └── LiveDataActivity.kt
│ │ │ └── rx
│ │ │ ├── RxJava2Activity.kt
│ │ │ └── RxJava3Activity.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_coroutines_exmaple.xml
│ │ ├── activity_example.xml
│ │ ├── activity_live_data.xml
│ │ ├── activity_main.xml
│ │ ├── activity_rx2_exmaple.xml
│ │ ├── activity_rx3_exmaple.xml
│ │ ├── fragment_child_container_example.xml
│ │ ├── fragment_child_example.xml
│ │ └── fragment_example.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── qifan
│ └── powerpermission
│ └── ExampleUnitTest.kt
├── build.gradle
├── dependencies.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── powerpermission-coroutines
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── qifan
│ │ └── powerpermission
│ │ └── coroutines
│ │ ├── CheckOnMainThread.kt
│ │ └── PowerPermissionCoroutines.kt
│ └── test
│ └── java
│ └── com
│ └── qifan
│ └── powerpermission
│ └── coroutines
│ └── ExampleUnitTest.kt
├── powerpermission-livedata
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── qifan
│ └── powerpermission
│ └── livedata
│ └── PowerPermissionLiveData.kt
├── powerpermission-rxjava2
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── qifan
│ └── powerpermission
│ └── rx2
│ ├── CheckOnMainThreadRx2.kt
│ └── PowerPermissionRx2.kt
├── powerpermission-rxjava3
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── qifan
│ └── powerpermission
│ └── rx3
│ ├── CheckOnMainThreadRx3.kt
│ └── PowerPermissionRx3.kt
├── powerpermission
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── qifan
│ │ └── powerpermission
│ │ ├── Activities.kt
│ │ ├── Fragments.kt
│ │ ├── PowerPermission.kt
│ │ ├── core
│ │ ├── PermissionFragment.kt
│ │ ├── PermissionRequestParams.kt
│ │ ├── PowerPermissionManager.kt
│ │ └── extension
│ │ │ ├── FragmentExt.kt
│ │ │ ├── LoggingExt.kt
│ │ │ └── PermissionExt.kt
│ │ ├── data
│ │ ├── Configuration.kt
│ │ ├── GrantResult.kt
│ │ ├── PermissionResult.kt
│ │ └── RationaleData.kt
│ │ └── rationale
│ │ ├── DialogRationale.kt
│ │ └── delegate
│ │ ├── RationaleActionCallback.kt
│ │ ├── RationaleDelegate.kt
│ │ └── dialog
│ │ └── DialogRationaleDelegate.kt
│ └── test
│ └── java
│ └── com
│ └── qifan
│ └── powerpermission
│ ├── PowerPermissionManager.kt
│ └── data
│ ├── GrantResultTest.kt
│ ├── PermissionResultTest.kt
│ └── RationaleDataTest.kt
├── publish-module.gradle
├── publish-root.gradle
├── settings.gradle
├── spotless
├── copyright.java
├── copyright.kt
└── spotless.gradle
└── version_plugin_config.gradle
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ci-gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2020 Qifan YANG (@underwindfall)
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | org.gradle.daemon=false
18 | org.gradle.parallel=true
19 | org.gradle.jvmargs=-Xmx5120m
20 | org.gradle.workers.max=2
21 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ### Guidelines
2 |
3 | 1. You must run the `spotlessApply` task before commiting, either through Android Studio or with `./gradlew spotlessApply`.
4 | 2. A PR should be focused and contained. If you are changing multiple unrelated things, they should be in separate PRs.
5 | 3. A PR should fix a bug or solve a problem - something that only you would use is not necessarily something that should be published.
6 | 4. Give your PR a detailed title and description - look over your code one last time before actually creating the PR. Give it a self-review.
7 |
8 | **If you do not follow the guidelines, your PR will be rejected.**
--------------------------------------------------------------------------------
/.github/workflows/check.yml:
--------------------------------------------------------------------------------
1 | name: Android Check CI
2 | on: [push]
3 |
4 | jobs:
5 | build:
6 |
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - name: Checkout code
11 | uses: actions/checkout@v2
12 |
13 | - name: Use Java11
14 | uses: actions/setup-java@v2
15 | with:
16 | distribution: adopt
17 | java-version: 11
18 |
19 | - name: Build with Gradle
20 | run: ./gradlew build check
21 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release And Update
2 | on:
3 | push:
4 | tags:
5 | - "v*"
6 |
7 | jobs:
8 | publish:
9 | name: Release APK
10 | runs-on: ubuntu-latest
11 | steps:
12 |
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Use Java11
17 | uses: actions/setup-java@v2
18 | with:
19 | distribution: adopt
20 | java-version: 11
21 |
22 | - name: Generate Debug APK
23 | run: ./gradlew clean assembleDebug
24 |
25 | - name: Upload APK
26 | uses: actions/upload-artifact@v1
27 | with:
28 | name: apk
29 | path: app/build/outputs/apk/debug/app-debug.apk
30 |
31 | - name: Get the version
32 | id: get_version
33 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
34 |
35 | - name: Create Release and Upload debug APK
36 | uses: underwindfall/create-release-with-debugapk@v1.2.4
37 | env:
38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39 | with:
40 | tag_name: ${{ steps.get_version.outputs.VERSION }}
41 | release_name: Release ${{ steps.get_version.outputs.VERSION }}
42 | asset_path: app/build/outputs/apk/debug/app-debug.apk
43 | asset_name: Example.apk
44 | asset_content_type: application/zip
45 | draft: false
46 | prerelease: false
47 |
48 | - name: Release build
49 | run: ./gradlew assembleRelease
50 |
51 | # Generates other artifacts (javadocJar is optional)
52 | - name: Source jar and dokka
53 | run: ./gradlew androidSourcesJar javadocJar
54 |
55 | - name: Publish Library MavenCenteral
56 | run: ./gradlew publishReleasePublicationToSonatypeRepository --max-workers 1 closeAndReleaseSonatypeStagingRepository
57 | env:
58 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
59 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
60 | SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
61 | SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
62 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
63 | SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
64 |
65 |
66 | - name: Notification Slack
67 | uses: homoluctus/slatify@v2.1.2
68 | if: always()
69 | with:
70 | type: ${{ job.status }}
71 | job_name: 'Deployment Notification'
72 | mention: 'here'
73 | mention_if: 'failure'
74 | icon_emoji: ':rocket:'
75 | channel: '#android-apps-website'
76 | url: ${{ secrets.SLACK_WEBHOOK_URL }}
77 | commit: true
78 | token: ${{ secrets.CI_LIB_TOKEN }}
79 |
80 |
--------------------------------------------------------------------------------
/.github/workflows/scheduled.yml:
--------------------------------------------------------------------------------
1 | name: Nightly tasks
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | timeout-minutes: 20
11 |
12 | env:
13 | TERM: dumb
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 |
18 | - name: set up JDK
19 | uses: actions/setup-java@v1
20 | with:
21 | java-version: 11
22 |
23 | - name: Copy CI gradle.properties
24 | run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
25 |
26 | - name: Check dependency updates
27 | run: ./gradlew dependencyUpdates
28 |
29 | - name: Upload dependency updates report
30 | uses: actions/upload-artifact@v1
31 | with:
32 | name: dependency-updates
33 | path: build/dependencyUpdates
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/android,androidstudio
3 | # Edit at https://www.gitignore.io/?templates=android,androidstudio
4 |
5 | ### Android ###
6 | # Built application files
7 | *.apk
8 | *.ap_
9 | *.aab
10 |
11 | # Files for the ART/Dalvik VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # Generated files
18 | bin/
19 | gen/
20 | out/
21 | release/
22 |
23 | # Gradle files
24 | .gradle/
25 | build/
26 |
27 | # Local configuration file (sdk path, etc)
28 | local.properties
29 |
30 | # Proguard folder generated by Eclipse
31 | proguard/
32 |
33 | # Log Files
34 | *.log
35 |
36 | # Android Studio Navigation editor temp files
37 | .navigation/
38 |
39 | # Android Studio captures folder
40 | captures/
41 |
42 | # IntelliJ
43 | *.iml
44 | .idea/workspace.xml
45 | .idea/tasks.xml
46 | .idea/gradle.xml
47 | .idea/assetWizardSettings.xml
48 | .idea/dictionaries
49 | .idea/libraries
50 | # Android Studio 3 in .gitignore file.
51 | .idea/caches
52 | .idea/modules.xml
53 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
54 | .idea/navEditor.xml
55 |
56 | # Keystore files
57 | # Uncomment the following lines if you do not want to check your keystore files in.
58 | #*.jks
59 | #*.keystore
60 |
61 | # External native build folder generated in Android Studio 2.2 and later
62 | .externalNativeBuild
63 |
64 | # Google Services (e.g. APIs or Firebase)
65 | # google-services.json
66 |
67 | # Freeline
68 | freeline.py
69 | freeline/
70 | freeline_project_description.json
71 |
72 | # fastlane
73 | fastlane/report.xml
74 | fastlane/Preview.html
75 | fastlane/screenshots
76 | fastlane/test_output
77 | fastlane/readme.md
78 |
79 | # Version control
80 | vcs.xml
81 |
82 | # lint
83 | lint/intermediates/
84 | lint/generated/
85 | lint/outputs/
86 | lint/tmp/
87 | # lint/reports/
88 |
89 | ### Android Patch ###
90 | gen-external-apklibs
91 | output.json
92 |
93 | # Replacement of .externalNativeBuild directories introduced
94 | # with Android Studio 3.5.
95 | .cxx/
96 |
97 | ### AndroidStudio ###
98 | # Covers files to be ignored for android development using Android Studio.
99 |
100 | # Built application files
101 |
102 | # Files for the ART/Dalvik VM
103 |
104 | # Java class files
105 |
106 | # Generated files
107 |
108 | # Gradle files
109 | .gradle
110 |
111 | # Signing files
112 | .signing/
113 |
114 | # Local configuration file (sdk path, etc)
115 |
116 | # Proguard folder generated by Eclipse
117 |
118 | # Log Files
119 |
120 | # Android Studio
121 | /*/build/
122 | /*/local.properties
123 | /*/out
124 | /*/*/build
125 | /*/*/production
126 | *.ipr
127 | *~
128 | *.swp
129 |
130 | # Android Patch
131 |
132 | # External native build folder generated in Android Studio 2.2 and later
133 |
134 | # NDK
135 | obj/
136 |
137 | # IntelliJ IDEA
138 | *.iws
139 | /out/
140 |
141 | # User-specific configurations
142 | .idea/caches/
143 | .idea/libraries/
144 | .idea/shelf/
145 | .idea/.name
146 | .idea/compiler.xml
147 | .idea/copyright/profiles_settings.xml
148 | .idea/encodings.xml
149 | .idea/misc.xml
150 | .idea/scopes/scope_settings.xml
151 | .idea/vcs.xml
152 | .idea/jsLibraryMappings.xml
153 | .idea/datasources.xml
154 | .idea/dataSources.ids
155 | .idea/sqlDataSources.xml
156 | .idea/dynamic.xml
157 | .idea/uiDesigner.xml
158 | .idea/*
159 |
160 | # OS-specific files
161 | .DS_Store
162 | .DS_Store?
163 | ._*
164 | .Spotlight-V100
165 | .Trashes
166 | ehthumbs.db
167 | Thumbs.db
168 |
169 | # Legacy Eclipse project files
170 | .classpath
171 | .project
172 | .cproject
173 | .settings/
174 |
175 | # Mobile Tools for Java (J2ME)
176 | .mtj.tmp/
177 |
178 | # Package Files #
179 | *.war
180 | *.ear
181 |
182 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
183 | hs_err_pid*
184 |
185 | ## Plugin-specific files:
186 |
187 | # mpeltonen/sbt-idea plugin
188 | .idea_modules/
189 |
190 | # JIRA plugin
191 | atlassian-ide-plugin.xml
192 |
193 | # Mongo Explorer plugin
194 | .idea/mongoSettings.xml
195 |
196 | # Crashlytics plugin (for Android Studio and IntelliJ)
197 | com_crashlytics_export_strings.xml
198 | crashlytics.properties
199 | crashlytics-build.properties
200 | fabric.properties
201 |
202 | ### AndroidStudio Patch ###
203 |
204 | !/gradle/wrapper/gradle-wrapper.jar
205 |
206 | # End of https://www.gitignore.io/api/android,androidstudio
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # ChangeLog
2 |
3 | ## v1.5.0
4 | * [CHANGE] Updated Deps and move target sdk to 31
5 | * [BREAK] From this version, we no longer support `Jcenter`, move to `mavenCentral`
6 |
7 |
8 | ## v1.4.0
9 | * fix kotlin migration potential issue [Wrong arguments execution order in calls with named vararg](https://youtrack.jetbrains.com/issue/KT-17691)
10 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at yangqifan02@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/README_ZH.md:
--------------------------------------------------------------------------------
1 | # PowerPermission
2 | [  ](https://api.bintray.com/packages/qifan/maven/powerpermission/_latestVersion)
3 | [](https://www.apache.org/licenses/LICENSE-2.0.html)
4 | 
5 | #### [English Documentation](https://github.com/underwindfall/PowerPermission) | 中文文档
6 |
7 | ## 目录
8 | - [PowerPermission](#powerpermission)
9 | * [目录](#--)
10 | * [简介](#--)
11 | * [配置方法](#----)
12 | + [基础库](#---)
13 | + [不同的其他接口配置](#---------)
14 | + [总结](#--)
15 | * [使用方法](#----)
16 | + [基础方法](#----)
17 | - [调用单例模式](#------)
18 | - [Extension 模式](#extension---)
19 | + [RxJava/RxKotlin 使用方法](#rxjava-rxkotlin-----)
20 | - [基础用法](#----)
21 | - [结合RxBinding使用](#--rxbinding--)
22 | + [Coroutine 使用方法](#coroutine-----)
23 | + [Livedata 使用方法](#livedata-----)
24 | + [Rationale Interface](#rationale-interface)
25 | - [自定义解释界面](#-------)
26 | - [Choose those permissions are rational](#choose-those-permissions-are-rational)
27 | * [License](#license)
28 |
29 | ## 简介
30 | `PowerPermission` 主要针对 `Android` 开发者在面对请求[RuntimePermission](https://developer.android.com/reference/java/lang/RuntimePermission)时的复杂流程的使用进行简化,来提高代码的便携效率。
31 | > 在这个Repo已经有个示例的`application`或者你可以通过这个[链接](https://github.com/underwindfall/PowerPermission/releases)直接下载APK.
32 | 它和其他的第三方请求库不同的有以下几点:
33 | - 可在 `AppCompatActivity` 和 `Fragment(包括ChildFragment)` 中请求
34 | - 不固定了用户拒绝权限后的说明内容表现形式,可自行化定制
35 | - 可自主选择那些权限再次显示解释界面
36 | - 支持多种接口配置(RxJava2和RxJava3,Coroutines,LiveData)
37 |
38 |
39 |
40 |
41 |
42 | ## 配置方法
43 | ### 基础库
44 | ```groovy
45 | implementation "com.qifan.powerpermission:powerpermission:1.5.0"
46 | ```
47 | ### 不同的其他接口配置
48 | ```groovy
49 | implementation "com.qifan.powerpermission:powerpermission-rxjava2:1.5.0"
50 | implementation "com.qifan.powerpermission:powerpermission-rxjava3:1.5.0"
51 | implementation "com.qifan.powerpermission:powerpermission-coroutines:1.5.0"
52 | implementation "com.qifan.powerpermission:powerpermission-livedata:1.5.0"
53 | ```
54 | ### 总结
55 | | Package Name | Role | Usage |
56 | | ------------- |-------------|-------------|
57 | | powerpermission | Basic core package | implementation "com.qifan.powerpermission:powerpermission:1.5.0" |
58 | | powerpermission-rxjava2 | Support RxJava2 | implementation "com.qifan.powerpermission:powerpermission-rxjava2:1.5.0" |
59 | | powerpermission-rxjava3 | Support RxJava3 | implementation "com.qifan.powerpermission:powerpermission-rxjava3:1.5.0" |
60 | | powerpermission-coroutines | Support Kotlin Coroutine | implementation "com.qifan.powerpermission:powerpermission-coroutines:1.5.0" |
61 | | powerpermission-livedata | Support Android LiveData | implementation "com.qifan.powerpermission:powerpermission-livedata:1.5.0" |
62 |
63 | ## 使用方法
64 |
65 | ### 基础方法
66 | #### 调用单例模式
67 | ```kotlin
68 | PowerPermission.init()
69 | .requestPermissions(
70 | context = this@ExampleActivity,
71 | permissions = *arrayOf(
72 | Manifest.permission.CAMERA
73 | )
74 | ) { permissionResult ->
75 | when {
76 | permissionResult.hasAllGranted() -> {
77 | doPermissionAllGrantedWork(permissionResult.granted())
78 | }
79 | permissionResult.hasRational() -> {
80 | doPermissionReasonWork(permissionResult.rational())
81 | }
82 | permissionResult.hasPermanentDenied() -> {
83 | doPermissionPermanentWork(permissionResult.permanentDenied())
84 | }
85 | }
86 | }
87 | ```
88 | #### Extension 模式
89 | - 在Activity请求
90 | ```kotlin
91 | askPermissions(
92 | Manifest.permission.CAMERA,
93 | Manifest.permission.READ_CALENDAR
94 | ) { permissionResult ->
95 | //do whatever you want
96 | }
97 | ```
98 | - 在Fragment请求
99 | ```kotlin
100 | askPermissions(
101 | Manifest.permission.CAMERA,
102 | Manifest.permission.READ_CALENDAR
103 | ) { permissionResult ->
104 | //do whatever you want
105 | }
106 | ```
107 | - 在ChildFragment中请求
108 | ```kotlin
109 | askPermissions(
110 | Manifest.permission.CAMERA,
111 | Manifest.permission.READ_CALENDAR
112 | ) { permissionResult ->
113 | //do whatever you want
114 | }
115 | ```
116 | ### RxJava/RxKotlin 使用方法
117 | #### 基础用法
118 | ```kotlin
119 | simpleRequestButton.setOnClickListener {
120 | askPermissionsRx(Manifest.permission.CAMERA)
121 | }
122 | ```
123 | #### 结合RxBinding使用
124 | ```kotlin
125 | rxBindingRequestButton.clicks()
126 | .throttleFirst(1L, TimeUnit.SECONDS)
127 | .flatMap {
128 | askPermissionsRx(Manifest.permission.CAMERA)
129 | }
130 | ```
131 |
132 | ### Coroutine 使用方法
133 | ```kotlin
134 | simpleRequestButton.setOnClickListener {
135 | launch {
136 | val result = awaitAskPermissions(
137 | Manifest.permission.CAMERA,
138 | rationaleDelegate = dialogRationaleDelegate
139 | )
140 | //do something for this permission result
141 | // handlePermissionRequest(result)
142 | }
143 | }
144 | ```
145 |
146 | ### Livedata 使用方法
147 | ```kotlin
148 | simpleRequestButton.setOnClickListener {
149 | observeAskPermissions(
150 | Manifest.permission.CAMERA,
151 | rationaleDelegate = dialogRationaleDelegate
152 | ).observe(this, Observer{ result->
153 | //do something for this permission result
154 | // handlePermissionRequest(result)
155 | })
156 | }
157 | ```
158 | ### Rationale Interface
159 | #### 自定义解释界面
160 | 在 `PowerPermission` 中它包含了一个 `interface` 叫做`RationaleDelegate`, 你可以把它当作一个桥梁来创造你的`Custom view`。
161 | 它提供了两个公开的方法,
162 | - displayRationale
163 | ```kotlin
164 | //aims to display a view to explain reason to user why request permissions
165 | fun displayRationale(
166 | vararg permission: Permission,
167 | message: String,
168 | actionCallback: RationaleActionCallback
169 | )
170 | ```
171 | - onDismissView
172 | ```kotlin
173 | //aims to simply disappear view and do some clean work etc.
174 | fun onDismissView()
175 | ```
176 | PS: `PowerPermission` 内置一个默认的 `DialogRationaleDelegate.kt` 来展示android经典的dialog界面向用户解释为什么我们需要这个`permission`的原因。
177 |
178 | #### Choose those permissions are rational
179 | - RationaleData
180 | 此外他还有个`data class`被用于向用户选择那些`permission`请求应该显示界面和相应的解释信息是什么。
181 | example:
182 | ```kotlin
183 | RationaleData(
184 | rationalPermission = Manifest.permission.CAMERA,
185 | message = message
186 | )
187 | RationaleData(
188 | rationalPermissions = listOf(Manifest.permission.CAMERA),
189 | message = message
190 | )
191 | ```
192 |
193 | ## License
194 | Copyright (C) 2020 Qifan Yang
195 | Licensed under the Apache License, Version 2.0 (the "License");
196 | you may not use this file except in compliance with the License.
197 | You may obtain a copy of the License at
198 |
199 | http://www.apache.org/licenses/LICENSE-2.0
200 |
201 | Unless required by applicable law or agreed to in writing, software
202 | distributed under the License is distributed on an "AS IS" BASIS,
203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
204 | See the License for the specific language governing permissions and
205 | limitations under the License.
206 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "kotlin-android"
3 |
4 | android {
5 | compileSdkVersion androidCompileSdkVersion
6 |
7 | defaultConfig {
8 | applicationId "com.qifan.powerpermission"
9 | minSdkVersion androidMinSdkVersion
10 | targetSdkVersion androidTargetSdkVersion
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | compileOptions {
18 | sourceCompatibility JavaVersion.VERSION_11
19 | targetCompatibility JavaVersion.VERSION_11
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
26 | }
27 | }
28 |
29 | buildFeatures {
30 | viewBinding = true
31 | }
32 | }
33 |
34 | dependencies {
35 | implementation fileTree(dir: "libs", include: ["*.jar"])
36 | implementation project(":powerpermission")
37 | implementation project(":powerpermission-rxjava2")
38 | implementation project(":powerpermission-rxjava3")
39 | implementation project(":powerpermission-coroutines")
40 | implementation project(":powerpermission-livedata")
41 | implementation kotlinStdlib
42 | implementation androidXAppCompat
43 | implementation constraintLayout
44 | implementation "com.jakewharton.rxbinding3:rxbinding:3.1.0"
45 | testImplementation "junit:junit:4.13.2"
46 | androidTestImplementation "androidx.test.ext:junit:1.1.3"
47 | androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0"
48 | }
49 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission
17 |
18 | import android.content.Intent
19 | import android.os.Bundle
20 | import android.view.View
21 | import androidx.appcompat.app.AppCompatActivity
22 | import com.qifan.powerpermission.activity.ExampleActivity
23 | import com.qifan.powerpermission.childfragment.ExampleChildContainerFragment
24 | import com.qifan.powerpermission.coroutines.CoroutinesActivity
25 | import com.qifan.powerpermission.databinding.ActivityMainBinding
26 | import com.qifan.powerpermission.fragment.ExampleFragment
27 | import com.qifan.powerpermission.livedata.LiveDataActivity
28 | import com.qifan.powerpermission.rx.RxJava2Activity
29 | import com.qifan.powerpermission.rx.RxJava3Activity
30 |
31 | class MainActivity : AppCompatActivity() {
32 | private lateinit var binding: ActivityMainBinding
33 | private val requestActivityButton get() = binding.requestActivity
34 | private val requestFragmentButton get() = binding.requestFragment
35 | private val requestChildFragmentButton get() = binding.requestChildFragment
36 | private val requestRxJava2Button get() = binding.requestRxjava2Activity
37 | private val requestRxJava3Button get() = binding.requestRxjava3Activity
38 | private val requestCoroutineButton get() = binding.requestCoroutineActivity
39 | private val requestLiveDataButton get() = binding.requestLivedataActivity
40 | private val requestFragmentContainer get() = binding.exampleFragmentContainer
41 |
42 | override fun onCreate(savedInstanceState: Bundle?) {
43 | super.onCreate(savedInstanceState)
44 | binding = ActivityMainBinding.inflate(layoutInflater)
45 | setContentView(binding.root)
46 | requestActivityButton.setOnClickListener { navigateToRequestActivity() }
47 | requestFragmentButton.setOnClickListener { navigateToRequestFragment() }
48 | requestChildFragmentButton.setOnClickListener { navigateToRequestChildFragment() }
49 | requestRxJava2Button.setOnClickListener { navigateToRxJava2Activity() }
50 | requestRxJava3Button.setOnClickListener { navigateToRxJava3Activity() }
51 | requestCoroutineButton.setOnClickListener { navigateToCoroutineActivity() }
52 | requestLiveDataButton.setOnClickListener { navigateToLiveDataActivity() }
53 | }
54 |
55 | private fun navigateToLiveDataActivity() {
56 | Intent(this@MainActivity, LiveDataActivity::class.java).apply {
57 | startActivity(this)
58 | }
59 | }
60 |
61 | private fun navigateToCoroutineActivity() {
62 | Intent(this@MainActivity, CoroutinesActivity::class.java).apply {
63 | startActivity(this)
64 | }
65 | }
66 |
67 | private fun navigateToRxJava3Activity() {
68 | Intent(this@MainActivity, RxJava3Activity::class.java).apply {
69 | startActivity(this)
70 | }
71 | }
72 |
73 | private fun navigateToRxJava2Activity() {
74 | Intent(this@MainActivity, RxJava2Activity::class.java).apply {
75 | startActivity(this)
76 | }
77 | }
78 |
79 | private fun navigateToRequestActivity() {
80 | Intent(this@MainActivity, ExampleActivity::class.java).apply {
81 | startActivity(this)
82 | }
83 | }
84 |
85 | private fun navigateToRequestFragment() {
86 | displayFragment(show = true)
87 | supportFragmentManager.beginTransaction()
88 | .add(R.id.example_fragment_container, ExampleFragment())
89 | .addToBackStack("RequestFragment")
90 | .commit()
91 | }
92 |
93 | private fun navigateToRequestChildFragment() {
94 | displayFragment(show = true)
95 | supportFragmentManager.beginTransaction()
96 | .add(R.id.example_fragment_container, ExampleChildContainerFragment())
97 | .addToBackStack("RequestChildFragment")
98 | .commit()
99 | }
100 |
101 | private fun displayFragment(show: Boolean) {
102 | if (show) {
103 | requestFragmentContainer.visibility = View.VISIBLE
104 | requestActivityButton.visibility = View.GONE
105 | requestFragmentButton.visibility = View.GONE
106 | requestChildFragmentButton.visibility = View.GONE
107 | requestRxJava2Button.visibility = View.GONE
108 | requestRxJava3Button.visibility = View.GONE
109 | requestCoroutineButton.visibility = View.GONE
110 | requestLiveDataButton.visibility = View.GONE
111 | } else {
112 | requestFragmentContainer.visibility = View.GONE
113 | requestActivityButton.visibility = View.VISIBLE
114 | requestFragmentButton.visibility = View.VISIBLE
115 | requestChildFragmentButton.visibility = View.VISIBLE
116 | requestRxJava2Button.visibility = View.VISIBLE
117 | requestRxJava3Button.visibility = View.VISIBLE
118 | requestCoroutineButton.visibility = View.VISIBLE
119 | requestLiveDataButton.visibility = View.VISIBLE
120 | supportActionBar?.setTitle(R.string.app_name)
121 | }
122 | }
123 |
124 | override fun onBackPressed() {
125 | if (supportFragmentManager.fragments.isNotEmpty()) {
126 | displayFragment(show = false)
127 | supportFragmentManager.popBackStack()
128 | } else {
129 | super.onBackPressed()
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/activity/ExampleActivity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.activity
17 |
18 | import android.Manifest
19 | import android.os.Bundle
20 | import androidx.appcompat.app.AppCompatActivity
21 | import com.qifan.powerpermission.Permission
22 | import com.qifan.powerpermission.PowerPermission
23 | import com.qifan.powerpermission.R
24 | import com.qifan.powerpermission.data.granted
25 | import com.qifan.powerpermission.data.hasAllGranted
26 | import com.qifan.powerpermission.data.hasPermanentDenied
27 | import com.qifan.powerpermission.data.hasRational
28 | import com.qifan.powerpermission.data.permanentDenied
29 | import com.qifan.powerpermission.data.rational
30 | import com.qifan.powerpermission.databinding.ActivityExampleBinding
31 |
32 | class ExampleActivity : AppCompatActivity() {
33 | private lateinit var binding: ActivityExampleBinding
34 | private val requestButton get() = binding.request
35 | private val resultText get() = binding.result
36 |
37 | override fun onCreate(savedInstanceState: Bundle?) {
38 | super.onCreate(savedInstanceState)
39 | binding = ActivityExampleBinding.inflate(layoutInflater)
40 | setContentView(binding.root)
41 | requestButton.setOnClickListener { requestPermissions() }
42 | supportActionBar?.setTitle(R.string.button_request_activity)
43 | }
44 |
45 | private fun requestPermissions() {
46 | PowerPermission.init()
47 | .requestPermissions(
48 | context = this@ExampleActivity,
49 | callback = { permissionResult ->
50 | when {
51 | permissionResult.hasAllGranted() -> {
52 | doPermissionAllGrantedWork(permissionResult.granted())
53 | }
54 | permissionResult.hasRational() -> {
55 | doPermissionReasonWork(permissionResult.rational())
56 | }
57 | permissionResult.hasPermanentDenied() -> {
58 | doPermissionPermanentWork(permissionResult.permanentDenied())
59 | }
60 | }
61 | },
62 | permissions = arrayOf(
63 | Manifest.permission.CAMERA
64 | )
65 | )
66 | }
67 |
68 | private fun doPermissionPermanentWork(permanentDenied: List) {
69 | resultText.text = getString(R.string.permission_denied, permanentDenied)
70 | }
71 |
72 | private fun doPermissionReasonWork(rational: List) {
73 | resultText.text = getString(R.string.permission_rational, rational)
74 | }
75 |
76 | private fun doPermissionAllGrantedWork(permissions: List) {
77 | resultText.text = getString(R.string.permission_all_granted, permissions)
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/childfragment/ExampleChildContainerFragment.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.childfragment
17 |
18 | import android.os.Bundle
19 | import android.view.LayoutInflater
20 | import android.view.View
21 | import android.view.ViewGroup
22 | import androidx.fragment.app.Fragment
23 | import com.qifan.powerpermission.R
24 | import com.qifan.powerpermission.databinding.FragmentChildContainerExampleBinding
25 |
26 | class ExampleChildContainerFragment : Fragment() {
27 | private lateinit var binding: FragmentChildContainerExampleBinding
28 |
29 | override fun onCreateView(
30 | inflater: LayoutInflater,
31 | container: ViewGroup?,
32 | savedInstanceState: Bundle?
33 | ): View? {
34 | binding = FragmentChildContainerExampleBinding.inflate(inflater, container, false)
35 | return binding.root
36 | }
37 |
38 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
39 | super.onViewCreated(view, savedInstanceState)
40 | childFragmentManager.beginTransaction()
41 | .add(R.id.child_container, ExampleChildFragment())
42 | .commit()
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/childfragment/ExampleChildFragment.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.childfragment
17 |
18 | import android.Manifest
19 | import android.os.Bundle
20 | import android.view.LayoutInflater
21 | import android.view.View
22 | import android.view.ViewGroup
23 | import androidx.appcompat.app.AppCompatActivity
24 | import androidx.fragment.app.Fragment
25 | import com.qifan.powerpermission.Permission
26 | import com.qifan.powerpermission.R
27 | import com.qifan.powerpermission.askPermissions
28 | import com.qifan.powerpermission.data.granted
29 | import com.qifan.powerpermission.data.hasAllGranted
30 | import com.qifan.powerpermission.data.hasPermanentDenied
31 | import com.qifan.powerpermission.data.hasRational
32 | import com.qifan.powerpermission.data.permanentDenied
33 | import com.qifan.powerpermission.data.rational
34 | import com.qifan.powerpermission.databinding.FragmentChildExampleBinding
35 | import com.qifan.powerpermission.rationale.createDialogRationale
36 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
37 |
38 | class ExampleChildFragment : Fragment() {
39 | private lateinit var binding: FragmentChildExampleBinding
40 | private val requestButton get() = binding.request
41 | private val resultText get() = binding.result
42 | private val dialogRationaleDelegate: RationaleDelegate by lazy {
43 | createDialogRationale(
44 | R.string.rational_title,
45 | Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
46 | getString(
47 | R.string.permission_rational,
48 | arrayOf(
49 | Manifest.permission.ACCESS_COARSE_LOCATION,
50 | Manifest.permission.ACCESS_FINE_LOCATION
51 | )
52 | )
53 | )
54 | }
55 |
56 | override fun onCreateView(
57 | inflater: LayoutInflater,
58 | container: ViewGroup?,
59 | savedInstanceState: Bundle?
60 | ): View? {
61 | binding = FragmentChildExampleBinding.inflate(inflater, container, false)
62 | return binding.root
63 | }
64 |
65 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
66 | super.onViewCreated(view, savedInstanceState)
67 | requestButton.setOnClickListener { requestPermissions() }
68 | (activity as AppCompatActivity?)?.supportActionBar?.setTitle(R.string.button_request_child_fragment)
69 | }
70 |
71 | private fun requestPermissions() {
72 | askPermissions(
73 | Manifest.permission.ACCESS_COARSE_LOCATION,
74 | Manifest.permission.ACCESS_FINE_LOCATION,
75 | rationaleDelegate = dialogRationaleDelegate
76 | ) { permissionResult ->
77 | when {
78 | permissionResult.hasAllGranted() -> {
79 | doPermissionAllGrantedWork(permissionResult.granted())
80 | }
81 | permissionResult.hasRational() -> {
82 | doPermissionReasonWork(permissionResult.rational())
83 | }
84 | permissionResult.hasPermanentDenied() -> {
85 | doPermissionPermanentWork(permissionResult.permanentDenied())
86 | }
87 | }
88 | }
89 | }
90 |
91 | private fun doPermissionPermanentWork(permanentDenied: List) {
92 | resultText.text = getString(R.string.permission_denied, permanentDenied)
93 | }
94 |
95 | private fun doPermissionReasonWork(rational: List) {
96 | resultText.text = getString(R.string.permission_rational, rational)
97 | }
98 |
99 | private fun doPermissionAllGrantedWork(permissions: List) {
100 | resultText.text = getString(R.string.permission_all_granted, permissions)
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/coroutines/CoroutinesActivity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.coroutines
17 |
18 | import android.Manifest
19 | import android.os.Bundle
20 | import androidx.appcompat.app.AppCompatActivity
21 | import com.qifan.powerpermission.Permission
22 | import com.qifan.powerpermission.R
23 | import com.qifan.powerpermission.data.PermissionResult
24 | import com.qifan.powerpermission.data.granted
25 | import com.qifan.powerpermission.data.hasAllGranted
26 | import com.qifan.powerpermission.data.hasPermanentDenied
27 | import com.qifan.powerpermission.data.hasRational
28 | import com.qifan.powerpermission.data.permanentDenied
29 | import com.qifan.powerpermission.data.rational
30 | import com.qifan.powerpermission.databinding.ActivityCoroutinesExmapleBinding
31 | import com.qifan.powerpermission.rationale.createDialogRationale
32 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
33 | import kotlinx.coroutines.CoroutineScope
34 | import kotlinx.coroutines.MainScope
35 | import kotlinx.coroutines.cancel
36 | import kotlinx.coroutines.launch
37 |
38 | class CoroutinesActivity : AppCompatActivity(), CoroutineScope by MainScope() {
39 | private lateinit var binding: ActivityCoroutinesExmapleBinding
40 | private val simpleRequestButton get() = binding.simpleRequest
41 | private val resultText get() = binding.result
42 | private val dialogRationaleDelegate: RationaleDelegate by lazy {
43 | createDialogRationale(
44 | R.string.rational_title,
45 | Manifest.permission.CAMERA,
46 | getString(R.string.permission_rational, Manifest.permission.CAMERA)
47 | )
48 | }
49 |
50 | override fun onCreate(savedInstanceState: Bundle?) {
51 | super.onCreate(savedInstanceState)
52 | binding = ActivityCoroutinesExmapleBinding.inflate(layoutInflater)
53 | setContentView(binding.root)
54 | supportActionBar?.setTitle(R.string.button_request_coroutines)
55 | setupRequestButton()
56 | }
57 |
58 | private fun setupRequestButton() {
59 | simpleRequestButton.setOnClickListener {
60 | launch {
61 | val result = awaitAskPermissions(
62 | Manifest.permission.CAMERA,
63 | rationaleDelegate = dialogRationaleDelegate
64 | )
65 | handlePermissionRequest(result)
66 | }
67 | }
68 | }
69 |
70 | private fun handlePermissionRequest(permissionResult: PermissionResult) {
71 | when {
72 | permissionResult.hasAllGranted() -> {
73 | doPermissionAllGrantedWork(permissionResult.granted())
74 | }
75 | permissionResult.hasRational() -> {
76 | doPermissionReasonWork(permissionResult.rational())
77 | }
78 | permissionResult.hasPermanentDenied() -> {
79 | doPermissionPermanentWork(permissionResult.permanentDenied())
80 | }
81 | }
82 | }
83 |
84 | private fun doPermissionPermanentWork(permanentDenied: List) {
85 | resultText.text = getString(R.string.permission_denied, permanentDenied)
86 | }
87 |
88 | private fun doPermissionReasonWork(rational: List) {
89 | resultText.text = getString(R.string.permission_rational, rational)
90 | }
91 |
92 | private fun doPermissionAllGrantedWork(permissions: List) {
93 | resultText.text = getString(R.string.permission_all_granted, permissions)
94 | }
95 |
96 | override fun onDestroy() {
97 | super.onDestroy()
98 | cancel()
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/fragment/ExampleFragment.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.fragment
17 |
18 | import android.Manifest
19 | import android.os.Bundle
20 | import android.view.LayoutInflater
21 | import android.view.View
22 | import android.view.ViewGroup
23 | import androidx.appcompat.app.AppCompatActivity
24 | import androidx.fragment.app.Fragment
25 | import com.qifan.powerpermission.Permission
26 | import com.qifan.powerpermission.R
27 | import com.qifan.powerpermission.askPermissions
28 | import com.qifan.powerpermission.data.granted
29 | import com.qifan.powerpermission.data.hasAllGranted
30 | import com.qifan.powerpermission.data.hasPermanentDenied
31 | import com.qifan.powerpermission.data.hasRational
32 | import com.qifan.powerpermission.data.permanentDenied
33 | import com.qifan.powerpermission.data.rational
34 | import com.qifan.powerpermission.databinding.FragmentExampleBinding
35 | import com.qifan.powerpermission.rationale.createDialogRationale
36 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
37 |
38 | class ExampleFragment : Fragment() {
39 | private lateinit var binding: FragmentExampleBinding
40 | private val requestButton get() = binding.request
41 | private val resultText get() = binding.result
42 | private val dialogRationaleDelegate: RationaleDelegate by lazy {
43 | createDialogRationale(
44 | R.string.rational_title,
45 | Manifest.permission.CALL_PHONE,
46 | getString(R.string.permission_rational, Manifest.permission.CALL_PHONE)
47 | )
48 | }
49 |
50 | override fun onCreateView(
51 | inflater: LayoutInflater,
52 | container: ViewGroup?,
53 | savedInstanceState: Bundle?
54 | ): View? {
55 | binding = FragmentExampleBinding.inflate(inflater, container, false)
56 | return binding.root
57 | }
58 |
59 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
60 | super.onViewCreated(view, savedInstanceState)
61 | requestButton.setOnClickListener { requestPermissions() }
62 | (activity as AppCompatActivity?)?.supportActionBar?.setTitle(R.string.button_request_fragment)
63 | }
64 |
65 | private fun requestPermissions() {
66 | askPermissions(
67 | Manifest.permission.CAMERA,
68 | Manifest.permission.READ_CALENDAR,
69 | rationaleDelegate = dialogRationaleDelegate
70 | ) { permissionResult ->
71 | when {
72 | permissionResult.hasAllGranted() -> {
73 | doPermissionAllGrantedWork(permissionResult.granted())
74 | }
75 | permissionResult.hasRational() -> {
76 | doPermissionReasonWork(permissionResult.rational())
77 | }
78 | permissionResult.hasPermanentDenied() -> {
79 | doPermissionPermanentWork(permissionResult.permanentDenied())
80 | }
81 | }
82 | }
83 | }
84 |
85 | private fun doPermissionPermanentWork(permanentDenied: List) {
86 | resultText.text = getString(R.string.permission_denied, permanentDenied)
87 | }
88 |
89 | private fun doPermissionReasonWork(rational: List) {
90 | resultText.text = getString(R.string.permission_rational, rational)
91 | }
92 |
93 | private fun doPermissionAllGrantedWork(permissions: List) {
94 | resultText.text = getString(R.string.permission_all_granted, permissions)
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/livedata/LiveDataActivity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.livedata
17 |
18 | import android.Manifest
19 | import android.os.Bundle
20 | import androidx.appcompat.app.AppCompatActivity
21 | import androidx.lifecycle.Observer
22 | import com.qifan.powerpermission.Permission
23 | import com.qifan.powerpermission.R
24 | import com.qifan.powerpermission.data.PermissionResult
25 | import com.qifan.powerpermission.data.granted
26 | import com.qifan.powerpermission.data.hasAllGranted
27 | import com.qifan.powerpermission.data.hasPermanentDenied
28 | import com.qifan.powerpermission.data.hasRational
29 | import com.qifan.powerpermission.data.permanentDenied
30 | import com.qifan.powerpermission.data.rational
31 | import com.qifan.powerpermission.databinding.ActivityLiveDataBinding
32 | import com.qifan.powerpermission.rationale.createDialogRationale
33 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
34 |
35 | class LiveDataActivity : AppCompatActivity() {
36 | private lateinit var binding: ActivityLiveDataBinding
37 | private val simpleRequestButton get() = binding.simpleRequest
38 | private val resultText get() = binding.result
39 | private val dialogRationaleDelegate: RationaleDelegate by lazy {
40 | createDialogRationale(
41 | R.string.rational_title,
42 | Manifest.permission.CAMERA,
43 | getString(R.string.permission_rational, Manifest.permission.CAMERA)
44 | )
45 | }
46 |
47 | override fun onCreate(savedInstanceState: Bundle?) {
48 | super.onCreate(savedInstanceState)
49 | binding = ActivityLiveDataBinding.inflate(layoutInflater)
50 | setContentView(binding.root)
51 | supportActionBar?.setTitle(R.string.button_request_livedata)
52 | setupRequestButton()
53 | }
54 |
55 | private fun setupRequestButton() {
56 | simpleRequestButton.setOnClickListener {
57 | observeAskPermissions(
58 | Manifest.permission.CAMERA,
59 | rationaleDelegate = dialogRationaleDelegate
60 | ).observe(this, Observer(::handlePermissionRequest))
61 | }
62 | }
63 |
64 | private fun handlePermissionRequest(permissionResult: PermissionResult) {
65 | when {
66 | permissionResult.hasAllGranted() -> {
67 | doPermissionAllGrantedWork(permissionResult.granted())
68 | }
69 | permissionResult.hasRational() -> {
70 | doPermissionReasonWork(permissionResult.rational())
71 | }
72 | permissionResult.hasPermanentDenied() -> {
73 | doPermissionPermanentWork(permissionResult.permanentDenied())
74 | }
75 | }
76 | }
77 |
78 | private fun doPermissionPermanentWork(permanentDenied: List) {
79 | resultText.text = getString(R.string.permission_denied, permanentDenied)
80 | }
81 |
82 | private fun doPermissionReasonWork(rational: List) {
83 | resultText.text = getString(R.string.permission_rational, rational)
84 | }
85 |
86 | private fun doPermissionAllGrantedWork(permissions: List) {
87 | resultText.text = getString(R.string.permission_all_granted, permissions)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/rx/RxJava2Activity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rx
17 |
18 | import android.Manifest
19 | import android.os.Bundle
20 | import android.util.Log
21 | import androidx.appcompat.app.AppCompatActivity
22 | import com.jakewharton.rxbinding3.view.clicks
23 | import com.qifan.powerpermission.Permission
24 | import com.qifan.powerpermission.R
25 | import com.qifan.powerpermission.data.PermissionResult
26 | import com.qifan.powerpermission.data.granted
27 | import com.qifan.powerpermission.data.hasAllGranted
28 | import com.qifan.powerpermission.data.hasPermanentDenied
29 | import com.qifan.powerpermission.data.hasRational
30 | import com.qifan.powerpermission.data.permanentDenied
31 | import com.qifan.powerpermission.data.rational
32 | import com.qifan.powerpermission.databinding.ActivityRx2ExmapleBinding
33 | import com.qifan.powerpermission.rationale.createDialogRationale
34 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
35 | import com.qifan.powerpermission.rx2.askPermissionsRx
36 | import io.reactivex.Completable
37 | import io.reactivex.disposables.CompositeDisposable
38 | import io.reactivex.disposables.Disposable
39 | import java.util.concurrent.TimeUnit
40 |
41 | class RxJava2Activity : AppCompatActivity() {
42 | private lateinit var binding: ActivityRx2ExmapleBinding
43 | private val simpleRequestButton get() = binding.simpleRequest
44 | private val rxBindingRequestButton get() = binding.requestRxBinding
45 | private val resultText get() = binding.result
46 |
47 | private val compositeDisposable: CompositeDisposable by lazy { CompositeDisposable() }
48 |
49 | private val dialogRationaleDelegate: RationaleDelegate by lazy {
50 | createDialogRationale(
51 | R.string.rational_title,
52 | Manifest.permission.CAMERA,
53 | getString(R.string.permission_rational, Manifest.permission.CAMERA)
54 | )
55 | }
56 |
57 | override fun onCreate(savedInstanceState: Bundle?) {
58 | super.onCreate(savedInstanceState)
59 | binding = ActivityRx2ExmapleBinding.inflate(layoutInflater)
60 | setContentView(binding.root)
61 | setUpSimpleRequestClick()
62 | setUpRxBindingClick()
63 | supportActionBar?.setTitle(R.string.button_request_rxjava2)
64 | }
65 |
66 | private fun setUpSimpleRequestClick() {
67 | simpleRequestButton.setOnClickListener {
68 | askPermissionsRx(
69 | Manifest.permission.CAMERA,
70 | rationaleDelegate = dialogRationaleDelegate
71 | )
72 | .switchMapCompletable(::handlePermissionRequest)
73 | .subscribeAndLogError()
74 | .let(compositeDisposable::add)
75 | }
76 | }
77 |
78 | private fun setUpRxBindingClick() {
79 | rxBindingRequestButton.clicks()
80 | .throttleFirst(1L, TimeUnit.SECONDS)
81 | .flatMap {
82 | askPermissionsRx(
83 | Manifest.permission.CAMERA,
84 | rationaleDelegate = dialogRationaleDelegate
85 | )
86 | }
87 | .switchMapCompletable(::handlePermissionRequest)
88 | .subscribeAndLogError()
89 | .let(compositeDisposable::add)
90 | }
91 |
92 | override fun onDestroy() {
93 | super.onDestroy()
94 | compositeDisposable.clear()
95 | }
96 |
97 | private fun handlePermissionRequest(permissionResult: PermissionResult): Completable {
98 | return Completable.fromCallable {
99 | when {
100 | permissionResult.hasAllGranted() -> {
101 | doPermissionAllGrantedWork(permissionResult.granted())
102 | }
103 | permissionResult.hasRational() -> {
104 | doPermissionReasonWork(permissionResult.rational())
105 | }
106 | permissionResult.hasPermanentDenied() -> {
107 | doPermissionPermanentWork(permissionResult.permanentDenied())
108 | }
109 | }
110 | }
111 | }
112 |
113 | private fun doPermissionPermanentWork(permanentDenied: List) {
114 | resultText.text = getString(R.string.permission_denied, permanentDenied)
115 | }
116 |
117 | private fun doPermissionReasonWork(rational: List) {
118 | resultText.text = getString(R.string.permission_rational, rational)
119 | }
120 |
121 | private fun doPermissionAllGrantedWork(permissions: List) {
122 | resultText.text = getString(R.string.permission_all_granted, permissions)
123 | }
124 |
125 | private fun logStreamError(exception: Throwable) {
126 | Log.e(RxJava2Activity::class.java.simpleName, exception.message, exception)
127 | }
128 |
129 | private fun Completable.subscribeAndLogError(): Disposable = subscribe({}, ::logStreamError)
130 | }
131 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qifan/powerpermission/rx/RxJava3Activity.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rx
17 |
18 | import android.Manifest
19 | import android.os.Bundle
20 | import android.util.Log
21 | import androidx.appcompat.app.AppCompatActivity
22 | import com.qifan.powerpermission.Permission
23 | import com.qifan.powerpermission.R
24 | import com.qifan.powerpermission.data.PermissionResult
25 | import com.qifan.powerpermission.data.granted
26 | import com.qifan.powerpermission.data.hasAllGranted
27 | import com.qifan.powerpermission.data.hasPermanentDenied
28 | import com.qifan.powerpermission.data.hasRational
29 | import com.qifan.powerpermission.data.permanentDenied
30 | import com.qifan.powerpermission.data.rational
31 | import com.qifan.powerpermission.databinding.ActivityRx3ExmapleBinding
32 | import com.qifan.powerpermission.rationale.createDialogRationale
33 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
34 | import com.qifan.powerpermission.rx3.askPermissionsRx
35 | import io.reactivex.rxjava3.core.Completable
36 | import io.reactivex.rxjava3.disposables.CompositeDisposable
37 | import io.reactivex.rxjava3.disposables.Disposable
38 |
39 | class RxJava3Activity : AppCompatActivity() {
40 | private lateinit var binding: ActivityRx3ExmapleBinding
41 | private val simpleRequestButton get() = binding.simpleRequest
42 | private val resultText get() = binding.result
43 |
44 | private val compositeDisposable: CompositeDisposable by lazy { CompositeDisposable() }
45 |
46 | private val dialogRationaleDelegate: RationaleDelegate by lazy {
47 | createDialogRationale(
48 | R.string.rational_title,
49 | Manifest.permission.CAMERA,
50 | getString(R.string.permission_rational, Manifest.permission.CAMERA)
51 | )
52 | }
53 |
54 | override fun onCreate(savedInstanceState: Bundle?) {
55 | super.onCreate(savedInstanceState)
56 | binding = ActivityRx3ExmapleBinding.inflate(layoutInflater)
57 | setContentView(binding.root)
58 | setUpSimpleRequestClick()
59 | supportActionBar?.setTitle(R.string.button_request_rxjava3)
60 | }
61 |
62 | private fun setUpSimpleRequestClick() {
63 | simpleRequestButton.setOnClickListener {
64 | askPermissionsRx(
65 | Manifest.permission.CAMERA,
66 | rationaleDelegate = dialogRationaleDelegate
67 | )
68 | .switchMapCompletable(::handlePermissionRequest)
69 | .subscribeAndLogError()
70 | .let(compositeDisposable::add)
71 | }
72 | }
73 |
74 | private fun handlePermissionRequest(permissionResult: PermissionResult): Completable {
75 | return Completable.fromCallable {
76 | when {
77 | permissionResult.hasAllGranted() -> {
78 | doPermissionAllGrantedWork(permissionResult.granted())
79 | }
80 | permissionResult.hasRational() -> {
81 | doPermissionReasonWork(permissionResult.rational())
82 | }
83 | permissionResult.hasPermanentDenied() -> {
84 | doPermissionPermanentWork(permissionResult.permanentDenied())
85 | }
86 | }
87 | }
88 | }
89 |
90 | private fun doPermissionPermanentWork(permanentDenied: List) {
91 | resultText.text = getString(R.string.permission_denied, permanentDenied)
92 | }
93 |
94 | private fun doPermissionReasonWork(rational: List) {
95 | resultText.text = getString(R.string.permission_rational, rational)
96 | }
97 |
98 | private fun doPermissionAllGrantedWork(permissions: List) {
99 | resultText.text = getString(R.string.permission_all_granted, permissions)
100 | }
101 |
102 | private fun logStreamError(exception: Throwable) {
103 | Log.e(RxJava2Activity::class.java.simpleName, exception.message, exception)
104 | }
105 |
106 | private fun Completable.subscribeAndLogError(): Disposable = subscribe({}, ::logStreamError)
107 |
108 | override fun onDestroy() {
109 | super.onDestroy()
110 | compositeDisposable.clear()
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
17 |
21 |
25 |
26 |
27 |
28 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
18 |
24 |
30 |
36 |
42 |
48 |
54 |
60 |
66 |
72 |
78 |
84 |
90 |
96 |
102 |
108 |
114 |
120 |
126 |
132 |
138 |
144 |
150 |
156 |
162 |
168 |
174 |
180 |
186 |
192 |
198 |
204 |
205 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_coroutines_exmaple.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
20 |
21 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_live_data.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
20 |
21 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
30 |
31 |
41 |
42 |
43 |
53 |
54 |
64 |
65 |
75 |
76 |
86 |
87 |
92 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_rx2_exmaple.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
20 |
21 |
31 |
32 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_rx3_exmaple.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
20 |
21 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_child_container_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_child_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PowerPermission
3 | All Permissions %s are granted.
4 | These Permissions %s are denied.
5 | These Permissions %s are needed for some reasons.
6 | Request Activity Permission
7 | Request Fragment Permission
8 | Request Child Fragment Permission
9 | Request with Rxjava2
10 | Request with Rxjava3
11 | Request with Coroutines
12 | Request with Livedata
13 | Permission rational title
14 | Request Permission
15 | RxBinding Request Permission
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/qifan/powerpermission/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission
17 |
18 | import org.junit.Assert.assertEquals
19 | import org.junit.Test
20 |
21 | /**
22 | * Example local unit test, which will execute on the development machine (host).
23 | *
24 | * See [testing documentation](http://d.android.com/tools/testing).
25 | */
26 | class ExampleUnitTest {
27 | @Test
28 | fun addition_isCorrect() {
29 | assertEquals(4, 2 + 2)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | apply plugin: "io.github.gradle-nexus.publish-plugin"
3 |
4 | buildscript {
5 | apply from: "dependencies.gradle"
6 | repositories {
7 | google()
8 | mavenCentral()
9 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
10 | maven { url 'https://plugins.gradle.org/m2/' }
11 | }
12 | dependencies {
13 | classpath "com.android.tools.build:gradle:7.0.4"
14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
15 | classpath "com.diffplug.spotless:spotless-plugin-gradle:5.17.1"
16 | classpath "de.mannodermaus.gradle.plugins:android-junit5:1.8.0.0"
17 | classpath "io.github.gradle-nexus:publish-plugin:1.1.0"
18 | // classpath "com.github.ben-manes:gradle-versions-plugin:0.36.0"
19 | }
20 | }
21 |
22 |
23 |
24 | apply {
25 | from(rootProject.file("spotless/spotless.gradle"))
26 | // from(rootProject.file("version_plugin_config.gradle"))
27 | }
28 |
29 | allprojects {
30 | repositories {
31 | google()
32 | mavenCentral()
33 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
34 | maven { url 'https://plugins.gradle.org/m2/' }
35 | }
36 | }
37 |
38 | subprojects {
39 | tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
40 | kotlinOptions {
41 | // Treat all Kotlin warnings as errors
42 | //TODO related issue https://github.com/underwindfall/PowerPermission/issues/30
43 | // allWarningsAsErrors = true
44 | jvmTarget = "11"
45 | }
46 | }
47 | }
48 |
49 | apply from: "publish-root.gradle"
50 |
--------------------------------------------------------------------------------
/dependencies.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | // APP VERSION
3 | androidVersionCode = 1
4 | androidVersionName = "1.4.1"
5 |
6 | // ANDROID VERSION
7 | androidCompileSdkVersion = 31
8 | androidMinSdkVersion = 21
9 | androidTargetSdkVersion = 31
10 |
11 | // KOTLIN
12 | kotlinVersion = "1.6.0"
13 | kotlinStdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
14 | kotlinReflect = "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
15 |
16 | // ANDROID LIB
17 | androidXVersion = "1.3.1"
18 | androidXAppCompat = "androidx.appcompat:appcompat:$androidXVersion"
19 | androidXAnnotations = "androidx.annotation:annotation:1.2.O"
20 | constraintLayoutVersion = "2.1.1"
21 | constraintLayout = "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
22 |
23 | //rx
24 | rxjava2Version = "2.2.21"
25 | rxjava3Version = "3.1.2"
26 | rxjava2 = "io.reactivex.rxjava2:rxjava:$rxjava2Version"
27 | rxjava3 = "io.reactivex.rxjava3:rxjava:$rxjava3Version"
28 |
29 | //coroutines
30 | coroutinesVerison = "1.5.2"
31 | coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVerison"
32 | coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVerison"
33 |
34 | //livedata
35 | livedataVersion = "2.4.0"
36 | livedata = "androidx.lifecycle:lifecycle-livedata:$livedataVersion"
37 |
38 | //spek
39 | spekVersion = "2.0.15"
40 | spekDslJvm = "org.spekframework.spek2:spek-dsl-jvm:$spekVersion"
41 | spekDslRunner = "org.spekframework.spek2:spek-runner-junit5:$spekVersion"
42 |
43 | //assertJ
44 | assertJCoreVerison = "3.18.1"
45 | assertJCore = "org.assertj:assertj-core:$assertJCoreVerison"
46 |
47 | //mock
48 | mockKVersion = "1.10.2"
49 | mockK = "io.mockk:mockk:$mockKVersion"
50 | }
51 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Nov 07 21:16:01 CET 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/powerpermission-coroutines/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/powerpermission-coroutines/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.library"
2 | apply plugin: "kotlin-android"
3 |
4 |
5 | android {
6 | compileSdkVersion androidCompileSdkVersion
7 |
8 | defaultConfig {
9 | minSdkVersion androidMinSdkVersion
10 | targetSdkVersion androidTargetSdkVersion
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | consumerProguardFiles "consumer-rules.pro"
13 | }
14 |
15 | compileOptions {
16 | sourceCompatibility JavaVersion.VERSION_11
17 | targetCompatibility JavaVersion.VERSION_11
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
24 | }
25 | }
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: "libs", include: ["*.jar"])
30 | implementation project(path: ":powerpermission")
31 | implementation kotlinStdlib
32 | implementation androidXAppCompat
33 | api coroutines
34 | api coroutinesAndroid
35 | testImplementation "junit:junit:4.13.1"
36 | androidTestImplementation "androidx.test.ext:junit:1.1.2"
37 | androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0"
38 | }
39 |
40 |
41 | ext {
42 | PUBLISH_GROUP_ID = "io.github.underwindfall"
43 | PUBLISH_VERSION = "1.5.0"
44 | PUBLISH_ARTIFACT_ID = 'powerpermission-coroutines'
45 | }
46 |
47 | apply from: "../publish-module.gradle"
48 |
--------------------------------------------------------------------------------
/powerpermission-coroutines/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/powerpermission-coroutines/consumer-rules.pro
--------------------------------------------------------------------------------
/powerpermission-coroutines/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/powerpermission-coroutines/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/powerpermission-coroutines/src/main/java/com/qifan/powerpermission/coroutines/CheckOnMainThread.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.coroutines
17 |
18 | import android.os.Looper
19 |
20 | internal fun checkMainThread() = check(Looper.myLooper() == Looper.getMainLooper()) {
21 | "Expected to be called on the main thread but was ${Thread.currentThread().name}"
22 | }
23 |
--------------------------------------------------------------------------------
/powerpermission-coroutines/src/main/java/com/qifan/powerpermission/coroutines/PowerPermissionCoroutines.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.coroutines
17 |
18 | import androidx.appcompat.app.AppCompatActivity
19 | import androidx.fragment.app.Fragment
20 | import com.qifan.powerpermission.Permission
21 | import com.qifan.powerpermission.RequestCode
22 | import com.qifan.powerpermission.askPermissions
23 | import com.qifan.powerpermission.askPermissionsAllGranted
24 | import com.qifan.powerpermission.core.PERMISSION_REQUEST_CODE
25 | import com.qifan.powerpermission.data.Configuration
26 | import com.qifan.powerpermission.data.DefaultConfiguration
27 | import com.qifan.powerpermission.data.PermissionResult
28 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
29 | import kotlinx.coroutines.suspendCancellableCoroutine
30 | import kotlin.coroutines.resume
31 |
32 | /**
33 | * extension to simple usage about requesting permissions
34 | * @param permissions a list of permissions to be requested
35 | * @param configuration custom settings for whole permission manager
36 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
37 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
38 | */
39 | suspend fun AppCompatActivity.awaitAskPermissions(
40 | vararg permissions: Permission,
41 | configuration: Configuration = DefaultConfiguration(),
42 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
43 | rationaleDelegate: RationaleDelegate? = null
44 | ): PermissionResult {
45 | checkMainThread()
46 | return suspendCancellableCoroutine { continuation ->
47 | askPermissions(
48 | configuration = configuration,
49 | requestCode = requestCode,
50 | rationaleDelegate = rationaleDelegate,
51 | callback = { result: PermissionResult ->
52 | continuation.resume(result)
53 | },
54 | permissions = permissions
55 | )
56 | }
57 | }
58 |
59 | /**
60 | * extension to simple usage about requesting permissions when all granted
61 | * @param permissions a list of permissions to be requested
62 | * @param configuration custom settings for whole permission manager
63 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
64 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
65 | */
66 | suspend fun AppCompatActivity.awaitAskPermissionsAllGranted(
67 | vararg permissions: Permission,
68 | configuration: Configuration = DefaultConfiguration(),
69 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
70 | rationaleDelegate: RationaleDelegate? = null
71 | ): Boolean {
72 | return suspendCancellableCoroutine { continuation ->
73 | askPermissionsAllGranted(
74 | configuration = configuration,
75 | requestCode = requestCode,
76 | rationaleDelegate = rationaleDelegate,
77 | callback = { allGranted: Boolean ->
78 | continuation.resume(allGranted)
79 | },
80 | permissions = permissions
81 | )
82 | }
83 | }
84 |
85 | /**
86 | * extension to simple usage about requesting permissions
87 | * @param permissions a list of permissions to be requested
88 | * @param configuration custom settings for whole permission manager
89 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
90 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
91 | */
92 | suspend fun Fragment.awaitAskPermissions(
93 | vararg permissions: Permission,
94 | configuration: Configuration = DefaultConfiguration(),
95 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
96 | rationaleDelegate: RationaleDelegate? = null
97 | ): PermissionResult {
98 | checkMainThread()
99 | return suspendCancellableCoroutine { continuation ->
100 | askPermissions(
101 | configuration = configuration,
102 | requestCode = requestCode,
103 | rationaleDelegate = rationaleDelegate,
104 | callback = { result: PermissionResult ->
105 | continuation.resume(result)
106 | },
107 | permissions = permissions
108 | )
109 | }
110 | }
111 |
112 | /**
113 | * extension to simple usage about requesting permissions when all granted
114 | * @param permissions a list of permissions to be requested
115 | * @param configuration custom settings for whole permission manager
116 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
117 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
118 | */
119 | suspend fun Fragment.awaitAskPermissionsAllGranted(
120 | vararg permissions: Permission,
121 | configuration: Configuration = DefaultConfiguration(),
122 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
123 | rationaleDelegate: RationaleDelegate? = null
124 | ): Boolean {
125 | return suspendCancellableCoroutine { continuation ->
126 | askPermissionsAllGranted(
127 | configuration = configuration,
128 | requestCode = requestCode,
129 | rationaleDelegate = rationaleDelegate,
130 | callback = { allGranted: Boolean ->
131 | continuation.resume(allGranted)
132 | },
133 | permissions = permissions
134 | )
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/powerpermission-coroutines/src/test/java/com/qifan/powerpermission/coroutines/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.coroutines
17 |
18 | import org.junit.Assert.assertEquals
19 | import org.junit.Test
20 |
21 | /**
22 | * Example local unit test, which will execute on the development machine (host).
23 | *
24 | * See [testing documentation](http://d.android.com/tools/testing).
25 | */
26 | class ExampleUnitTest {
27 | @Test
28 | fun addition_isCorrect() {
29 | assertEquals(4, 2 + 2)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/powerpermission-livedata/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/powerpermission-livedata/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.library"
2 | apply plugin: "kotlin-android"
3 |
4 | android {
5 | compileSdkVersion androidCompileSdkVersion
6 |
7 | defaultConfig {
8 | minSdkVersion androidMinSdkVersion
9 | targetSdkVersion androidTargetSdkVersion
10 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
11 | consumerProguardFiles "consumer-rules.pro"
12 | }
13 |
14 | compileOptions {
15 | sourceCompatibility JavaVersion.VERSION_11
16 | targetCompatibility JavaVersion.VERSION_11
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
23 | }
24 | }
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 | implementation project(path: ":powerpermission")
30 | implementation kotlinStdlib
31 | implementation androidXAppCompat
32 | api livedata
33 | testImplementation "junit:junit:4.13.2"
34 | }
35 |
36 | ext {
37 | PUBLISH_GROUP_ID = "io.github.underwindfall"
38 | PUBLISH_VERSION = "1.5.0"
39 | PUBLISH_ARTIFACT_ID = 'powerpermission-livedata'
40 | }
41 |
42 | apply from: "../publish-module.gradle"
43 |
--------------------------------------------------------------------------------
/powerpermission-livedata/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/powerpermission-livedata/consumer-rules.pro
--------------------------------------------------------------------------------
/powerpermission-livedata/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/powerpermission-livedata/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/powerpermission-livedata/src/main/java/com/qifan/powerpermission/livedata/PowerPermissionLiveData.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.livedata
17 |
18 | import androidx.appcompat.app.AppCompatActivity
19 | import androidx.fragment.app.Fragment
20 | import androidx.lifecycle.MutableLiveData
21 | import com.qifan.powerpermission.Permission
22 | import com.qifan.powerpermission.RequestCode
23 | import com.qifan.powerpermission.askPermissions
24 | import com.qifan.powerpermission.askPermissionsAllGranted
25 | import com.qifan.powerpermission.core.PERMISSION_REQUEST_CODE
26 | import com.qifan.powerpermission.data.Configuration
27 | import com.qifan.powerpermission.data.DefaultConfiguration
28 | import com.qifan.powerpermission.data.PermissionResult
29 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
30 |
31 | /**
32 | * extension to simple usage about requesting permissions
33 | * @param permissions a list of permissions to be requested
34 | * @param configuration custom settings for whole permission manager
35 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
36 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
37 | */
38 | fun AppCompatActivity.observeAskPermissions(
39 | vararg permissions: Permission,
40 | configuration: Configuration = DefaultConfiguration(),
41 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
42 | rationaleDelegate: RationaleDelegate? = null
43 | ): MutableLiveData {
44 | val mLiveData = MutableLiveData()
45 | askPermissions(
46 | configuration = configuration,
47 | requestCode = requestCode,
48 | rationaleDelegate = rationaleDelegate,
49 | callback = { result: PermissionResult ->
50 | mLiveData.postValue(result)
51 | },
52 | permissions = permissions
53 | )
54 | return mLiveData
55 | }
56 |
57 | /**
58 | * extension to simple usage about requesting permissions when all granted
59 | * @param permissions a list of permissions to be requested
60 | * @param configuration custom settings for whole permission manager
61 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
62 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
63 | */
64 | fun AppCompatActivity.observeAskPermissionsAllGranted(
65 | vararg permissions: Permission,
66 | configuration: Configuration = DefaultConfiguration(),
67 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
68 | rationaleDelegate: RationaleDelegate? = null
69 | ): MutableLiveData {
70 | val mLiveData = MutableLiveData()
71 | askPermissionsAllGranted(
72 | configuration = configuration,
73 | requestCode = requestCode,
74 | callback = { allGranted: Boolean ->
75 | mLiveData.postValue(allGranted)
76 | },
77 | rationaleDelegate = rationaleDelegate,
78 | permissions = permissions
79 | )
80 | return mLiveData
81 | }
82 |
83 | /**
84 | * extension to simple usage about requesting permissions
85 | * @param permissions a list of permissions to be requested
86 | * @param configuration custom settings for whole permission manager
87 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
88 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
89 | */
90 | fun Fragment.observeAskPermissions(
91 | vararg permissions: Permission,
92 | configuration: Configuration = DefaultConfiguration(),
93 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
94 | rationaleDelegate: RationaleDelegate? = null
95 | ): MutableLiveData {
96 | val mLiveData = MutableLiveData()
97 | askPermissions(
98 | configuration = configuration,
99 | requestCode = requestCode,
100 | rationaleDelegate = rationaleDelegate,
101 | callback = { result: PermissionResult ->
102 | mLiveData.postValue(result)
103 | },
104 | permissions = permissions
105 | )
106 | return mLiveData
107 | }
108 |
109 | /**
110 | * extension to simple usage about requesting permissions when all granted
111 | * @param permissions a list of permissions to be requested
112 | * @param configuration custom settings for whole permission manager
113 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
114 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
115 | */
116 | fun Fragment.observeAskPermissionsAllGranted(
117 | vararg permissions: Permission,
118 | configuration: Configuration = DefaultConfiguration(),
119 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
120 | rationaleDelegate: RationaleDelegate? = null
121 | ): MutableLiveData {
122 | val mLiveData = MutableLiveData()
123 | askPermissionsAllGranted(
124 | configuration = configuration,
125 | requestCode = requestCode,
126 | rationaleDelegate = rationaleDelegate,
127 | callback = { allGranted: Boolean ->
128 | mLiveData.postValue(allGranted)
129 | },
130 | permissions = permissions
131 | )
132 | return mLiveData
133 | }
134 |
--------------------------------------------------------------------------------
/powerpermission-rxjava2/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/powerpermission-rxjava2/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.library"
2 | apply plugin: "kotlin-android"
3 |
4 | android {
5 | compileSdkVersion androidCompileSdkVersion
6 |
7 | defaultConfig {
8 | minSdkVersion androidMinSdkVersion
9 | targetSdkVersion androidTargetSdkVersion
10 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
11 | consumerProguardFiles "consumer-rules.pro"
12 | }
13 |
14 | compileOptions {
15 | sourceCompatibility JavaVersion.VERSION_11
16 | targetCompatibility JavaVersion.VERSION_11
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
23 | }
24 | }
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: "libs", include: ["*.jar"])
29 | implementation project(path: ":powerpermission")
30 | implementation kotlinStdlib
31 | implementation androidXAppCompat
32 | api rxjava2
33 | testImplementation "junit:junit:4.13.1"
34 | }
35 |
36 |
37 | ext {
38 | PUBLISH_GROUP_ID = "io.github.underwindfall"
39 | PUBLISH_VERSION = "1.5.0"
40 | PUBLISH_ARTIFACT_ID = 'powerpermission-rxjava2'
41 | }
42 |
43 | apply from: "../publish-module.gradle"
44 |
--------------------------------------------------------------------------------
/powerpermission-rxjava2/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/powerpermission-rxjava2/consumer-rules.pro
--------------------------------------------------------------------------------
/powerpermission-rxjava2/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/powerpermission-rxjava2/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/powerpermission-rxjava2/src/main/java/com/qifan/powerpermission/rx2/CheckOnMainThreadRx2.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rx2
17 |
18 | import android.os.Looper
19 |
20 | internal fun checkMainThread() = check(Looper.myLooper() == Looper.getMainLooper()) {
21 | "Expected to be called on the main thread but was ${Thread.currentThread().name}"
22 | }
23 |
--------------------------------------------------------------------------------
/powerpermission-rxjava2/src/main/java/com/qifan/powerpermission/rx2/PowerPermissionRx2.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rx2
17 |
18 | import androidx.appcompat.app.AppCompatActivity
19 | import androidx.fragment.app.Fragment
20 | import com.qifan.powerpermission.Permission
21 | import com.qifan.powerpermission.RequestCode
22 | import com.qifan.powerpermission.askPermissions
23 | import com.qifan.powerpermission.askPermissionsAllGranted
24 | import com.qifan.powerpermission.core.PERMISSION_REQUEST_CODE
25 | import com.qifan.powerpermission.data.Configuration
26 | import com.qifan.powerpermission.data.DefaultConfiguration
27 | import com.qifan.powerpermission.data.PermissionResult
28 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
29 | import io.reactivex.Observable
30 |
31 | /**
32 | * extension to simple usage about requesting permissions
33 | * @param permissions a list of permissions to be requested
34 | * @param configuration custom settings for whole permission manager
35 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
36 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
37 | * @return [Observable] permission result
38 | */
39 | fun AppCompatActivity.askPermissionsRx(
40 | vararg permissions: Permission,
41 | configuration: Configuration = DefaultConfiguration(),
42 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
43 | rationaleDelegate: RationaleDelegate? = null
44 | ): Observable {
45 | checkMainThread()
46 | return Observable.create { emitter ->
47 | askPermissions(
48 | configuration = configuration,
49 | requestCode = requestCode,
50 | rationaleDelegate = rationaleDelegate,
51 | callback = { emitter.onNext(it) },
52 | permissions = permissions
53 | )
54 | }
55 | }
56 |
57 | /**
58 | * extension to simple usage about requesting permissions when all granted
59 | * @param permissions a list of permissions to be requested
60 | * @param configuration custom settings for whole permission manager
61 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
62 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
63 | * @return [Observable] true means all permissions granted otherwise not
64 | */
65 | fun AppCompatActivity.askPermissionsAllGrantedRx(
66 | vararg permissions: Permission,
67 | configuration: Configuration = DefaultConfiguration(),
68 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
69 | rationaleDelegate: RationaleDelegate? = null
70 | ): Observable {
71 | return Observable.create { emitter ->
72 | askPermissionsAllGranted(
73 | configuration = configuration,
74 | requestCode = requestCode,
75 | rationaleDelegate = rationaleDelegate,
76 | callback = { allGranted: Boolean ->
77 | emitter.onNext(allGranted)
78 | },
79 | permissions = permissions
80 | )
81 | }
82 | }
83 |
84 | /**
85 | * extension to simple usage about requesting permissions
86 | * @param permissions a list of permissions to be requested
87 | * @param configuration custom settings for whole permission manager
88 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
89 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
90 | * @return [Observable] permission result
91 | */
92 | fun Fragment.askPermissionsRx(
93 | vararg permissions: Permission,
94 | configuration: Configuration = DefaultConfiguration(),
95 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
96 | rationaleDelegate: RationaleDelegate? = null
97 | ): Observable {
98 | checkMainThread()
99 | return Observable.create { emitter ->
100 | askPermissions(
101 | configuration = configuration,
102 | requestCode = requestCode,
103 | rationaleDelegate = rationaleDelegate,
104 | callback = { emitter.onNext(it) },
105 | permissions = permissions,
106 | )
107 | }
108 | }
109 |
110 | /**
111 | * extension to simple usage about requesting permissions when all granted
112 | * @param permissions a list of permissions to be requested
113 | * @param configuration custom settings for whole permission manager
114 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
115 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
116 | * @return [Observable] true means all permissions granted otherwise not
117 | */
118 | fun Fragment.askPermissionsAllGrantedRx(
119 | vararg permissions: Permission,
120 | configuration: Configuration = DefaultConfiguration(),
121 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
122 | rationaleDelegate: RationaleDelegate? = null
123 | ): Observable {
124 | return Observable.create { emitter ->
125 | askPermissionsAllGranted(
126 | configuration = configuration,
127 | requestCode = requestCode,
128 | rationaleDelegate = rationaleDelegate,
129 | callback = { allGranted: Boolean ->
130 | emitter.onNext(allGranted)
131 | },
132 | permissions = permissions
133 | )
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/powerpermission-rxjava3/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/powerpermission-rxjava3/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.library"
2 | apply plugin: "kotlin-android"
3 |
4 | android {
5 | compileSdkVersion androidCompileSdkVersion
6 |
7 | defaultConfig {
8 | minSdkVersion androidMinSdkVersion
9 | targetSdkVersion androidTargetSdkVersion
10 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
11 | consumerProguardFiles "consumer-rules.pro"
12 | }
13 |
14 | compileOptions {
15 | sourceCompatibility JavaVersion.VERSION_11
16 | targetCompatibility JavaVersion.VERSION_11
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
23 | }
24 | }
25 |
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: "libs", include: ["*.jar"])
30 | implementation project(path: ":powerpermission")
31 | implementation kotlinStdlib
32 | implementation androidXAppCompat
33 | api rxjava3
34 | testImplementation "junit:junit:4.13.2"
35 | }
36 |
37 |
38 | ext {
39 | PUBLISH_GROUP_ID = "io.github.underwindfall"
40 | PUBLISH_VERSION = "1.5.0"
41 | PUBLISH_ARTIFACT_ID = 'powerpermission-rxjava3'
42 | }
43 |
44 | apply from: "../publish-module.gradle"
45 |
--------------------------------------------------------------------------------
/powerpermission-rxjava3/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/powerpermission-rxjava3/consumer-rules.pro
--------------------------------------------------------------------------------
/powerpermission-rxjava3/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/powerpermission-rxjava3/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/powerpermission-rxjava3/src/main/java/com/qifan/powerpermission/rx3/CheckOnMainThreadRx3.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rx3
17 |
18 | import android.os.Looper
19 |
20 | internal fun checkMainThread() = check(Looper.myLooper() == Looper.getMainLooper()) {
21 | "Expected to be called on the main thread but was ${Thread.currentThread().name}"
22 | }
23 |
--------------------------------------------------------------------------------
/powerpermission-rxjava3/src/main/java/com/qifan/powerpermission/rx3/PowerPermissionRx3.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rx3
17 |
18 | import androidx.appcompat.app.AppCompatActivity
19 | import androidx.fragment.app.Fragment
20 | import com.qifan.powerpermission.Permission
21 | import com.qifan.powerpermission.RequestCode
22 | import com.qifan.powerpermission.askPermissions
23 | import com.qifan.powerpermission.askPermissionsAllGranted
24 | import com.qifan.powerpermission.core.PERMISSION_REQUEST_CODE
25 | import com.qifan.powerpermission.data.Configuration
26 | import com.qifan.powerpermission.data.DefaultConfiguration
27 | import com.qifan.powerpermission.data.PermissionResult
28 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
29 | import io.reactivex.rxjava3.core.Observable
30 |
31 | /**
32 | * extension to simple usage about requesting permissions
33 | * @param permissions a list of permissions to be requested
34 | * @param configuration custom settings for whole permission manager
35 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
36 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
37 | * @return [Observable] permission result
38 | */
39 | fun AppCompatActivity.askPermissionsRx(
40 | vararg permissions: Permission,
41 | configuration: Configuration = DefaultConfiguration(),
42 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
43 | rationaleDelegate: RationaleDelegate? = null
44 | ): Observable {
45 | checkMainThread()
46 | return Observable.create { emitter ->
47 | askPermissions(
48 | configuration = configuration,
49 | requestCode = requestCode,
50 | rationaleDelegate = rationaleDelegate,
51 | callback = { emitter.onNext(it) },
52 | permissions = permissions
53 | )
54 | }
55 | }
56 |
57 | /**
58 | * extension to simple usage about requesting permissions when all granted
59 | * @param permissions a list of permissions to be requested
60 | * @param configuration custom settings for whole permission manager
61 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
62 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
63 | * @return [Observable] true means all permissions granted otherwise not
64 | */
65 | fun AppCompatActivity.askPermissionsAllGrantedRx(
66 | configuration: Configuration = DefaultConfiguration(),
67 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
68 | rationaleDelegate: RationaleDelegate? = null,
69 | vararg permissions: Permission
70 | ): Observable {
71 | return Observable.create { emitter ->
72 | askPermissionsAllGranted(
73 | configuration = configuration,
74 | requestCode = requestCode,
75 | rationaleDelegate = rationaleDelegate,
76 | callback = { allGranted: Boolean ->
77 | emitter.onNext(allGranted)
78 | },
79 | permissions = permissions
80 | )
81 | }
82 | }
83 |
84 | /**
85 | * extension to simple usage about requesting permissions
86 | * @param permissions a list of permissions to be requested
87 | * @param configuration custom settings for whole permission manager
88 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
89 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
90 | * @return [Observable] permission result
91 | */
92 | fun Fragment.askPermissionsRx(
93 | vararg permissions: Permission,
94 | configuration: Configuration = DefaultConfiguration(),
95 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
96 | rationaleDelegate: RationaleDelegate? = null
97 | ): Observable {
98 | checkMainThread()
99 | return Observable.create { emitter ->
100 | askPermissions(
101 | configuration = configuration,
102 | requestCode = requestCode,
103 | rationaleDelegate = rationaleDelegate,
104 | callback = { emitter.onNext(it) },
105 | permissions = permissions
106 | )
107 | }
108 | }
109 |
110 | /**
111 | * extension to simple usage about requesting permissions when all granted
112 | * @param permissions a list of permissions to be requested
113 | * @param configuration custom settings for whole permission manager
114 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
115 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
116 | * @return [Observable] true means all permissions granted otherwise not
117 | */
118 | fun Fragment.askPermissionsAllGrantedRx(
119 | vararg permissions: Permission,
120 | configuration: Configuration = DefaultConfiguration(),
121 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
122 | rationaleDelegate: RationaleDelegate? = null
123 | ): Observable {
124 | return Observable.create { emitter ->
125 | askPermissionsAllGranted(
126 | configuration = configuration,
127 | requestCode = requestCode,
128 | rationaleDelegate = rationaleDelegate,
129 | callback = { allGranted: Boolean ->
130 | emitter.onNext(allGranted)
131 | },
132 | permissions = permissions
133 | )
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/powerpermission/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/powerpermission/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.library"
2 | apply plugin: "kotlin-android"
3 | apply plugin: "de.mannodermaus.android-junit5"
4 |
5 | android {
6 | compileSdkVersion androidCompileSdkVersion
7 |
8 | defaultConfig {
9 | minSdkVersion androidMinSdkVersion
10 | targetSdkVersion androidTargetSdkVersion
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | consumerProguardFiles "consumer-rules.pro"
13 | }
14 |
15 | compileOptions {
16 | sourceCompatibility JavaVersion.VERSION_11
17 | targetCompatibility JavaVersion.VERSION_11
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
24 | }
25 | }
26 |
27 | testOptions {
28 | junitPlatform {
29 | filters {
30 | engines {
31 | include 'spek2'
32 | }
33 | }
34 | jacocoOptions {
35 | // here goes all jacoco config, for example
36 | html.enabled = true
37 | xml.enabled = false
38 | csv.enabled = false
39 | }
40 | }
41 | unitTests.all {
42 | testLogging.events = ["passed", "skipped", "failed"]
43 | }
44 | }
45 | }
46 |
47 | dependencies {
48 | implementation fileTree(dir: "libs", include: ["*.jar"])
49 | implementation kotlinStdlib
50 | implementation kotlinReflect
51 | implementation androidXAppCompat
52 | compileOnly androidXAnnotations
53 | // spek
54 | testImplementation assertJCore
55 | testImplementation mockK
56 | testImplementation spekDslJvm
57 | testImplementation spekDslRunner
58 | }
59 |
60 | ext {
61 | PUBLISH_GROUP_ID = "io.github.underwindfall"
62 | PUBLISH_VERSION = "1.5.0"
63 | PUBLISH_ARTIFACT_ID = 'powerpermission'
64 | }
65 |
66 | apply from: "../publish-module.gradle"
67 |
--------------------------------------------------------------------------------
/powerpermission/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/underwindfall/PowerPermission/4e34811096c139d06267f18b09e63e81d76f34f4/powerpermission/consumer-rules.pro
--------------------------------------------------------------------------------
/powerpermission/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/powerpermission/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/Activities.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission
17 |
18 | import androidx.appcompat.app.AppCompatActivity
19 | import com.qifan.powerpermission.core.PERMISSION_REQUEST_CODE
20 | import com.qifan.powerpermission.data.Configuration
21 | import com.qifan.powerpermission.data.DefaultConfiguration
22 | import com.qifan.powerpermission.data.hasAllGranted
23 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
24 |
25 | /**
26 | * extension to simple usage about requesting permissions
27 | * @param permissions a list of permissions to be requested
28 | * @param configuration custom settings for whole permission manager
29 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
30 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
31 | * @param callback to return after execute requesting permissions
32 | */
33 | fun AppCompatActivity.askPermissions(
34 | vararg permissions: Permission,
35 | configuration: Configuration = DefaultConfiguration(),
36 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
37 | rationaleDelegate: RationaleDelegate? = null,
38 | callback: PermissionCallback
39 | ) {
40 | PowerPermission.init(configuration)
41 | .requestPermissions(
42 | context = this,
43 | requestCode = requestCode,
44 | rationaleDelegate = rationaleDelegate,
45 | callback = callback,
46 | permissions = permissions
47 | )
48 | }
49 |
50 | /**
51 | * extension to simple usage about requesting permissions when all granted
52 | * @param permissions a list of permissions to be requested
53 | * @param configuration custom settings for whole permission manager
54 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
55 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
56 | * @param callback to return after execute requesting permissions all granted or not
57 | */
58 | fun AppCompatActivity.askPermissionsAllGranted(
59 | vararg permissions: Permission,
60 | configuration: Configuration = DefaultConfiguration(),
61 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
62 | rationaleDelegate: RationaleDelegate? = null,
63 | callback: (Boolean) -> Unit
64 | ) {
65 | PowerPermission.init(configuration)
66 | .requestPermissions(
67 | context = this,
68 | requestCode = requestCode,
69 | rationaleDelegate = rationaleDelegate,
70 | callback = { permissionResults ->
71 | callback(permissionResults.hasAllGranted())
72 | },
73 | permissions = permissions
74 | )
75 | }
76 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/Fragments.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission
17 |
18 | import androidx.fragment.app.Fragment
19 | import com.qifan.powerpermission.core.PERMISSION_REQUEST_CODE
20 | import com.qifan.powerpermission.data.Configuration
21 | import com.qifan.powerpermission.data.DefaultConfiguration
22 | import com.qifan.powerpermission.data.hasAllGranted
23 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
24 |
25 | /**
26 | * extension to simple usage about requesting permissions
27 | * @param permissions a list of permissions to be requested
28 | * @param configuration custom settings for whole permission manager
29 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
30 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
31 | * @param callback to return after execute requesting permissions
32 | */
33 | fun Fragment.askPermissions(
34 | vararg permissions: Permission,
35 | configuration: Configuration = DefaultConfiguration(),
36 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
37 | rationaleDelegate: RationaleDelegate? = null,
38 | callback: PermissionCallback,
39 | ) {
40 | PowerPermission.init(configuration)
41 | .requestPermissions(
42 | context = this,
43 | requestCode = requestCode,
44 | rationaleDelegate = rationaleDelegate,
45 | callback = callback,
46 | permissions = permissions
47 | )
48 | }
49 |
50 | /**
51 | * extension to simple usage about requesting permissions when all granted
52 | * @param permissions a list of permissions to be requested
53 | * @param configuration custom settings for whole permission manager
54 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
55 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
56 | * @param callback to return after execute requesting permissions all granted or not
57 | */
58 | fun Fragment.askPermissionsAllGranted(
59 | vararg permissions: Permission,
60 | configuration: Configuration = DefaultConfiguration(),
61 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
62 | rationaleDelegate: RationaleDelegate? = null,
63 | callback: (Boolean) -> Unit
64 | ) {
65 | PowerPermission.init(configuration)
66 | .requestPermissions(
67 | context = this,
68 | requestCode = requestCode,
69 | rationaleDelegate = rationaleDelegate,
70 | callback = { permissionResults ->
71 | callback(permissionResults.hasAllGranted())
72 | },
73 | permissions = permissions
74 | )
75 | }
76 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/PowerPermission.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission
17 |
18 | import com.qifan.powerpermission.core.PowerPermissionManager
19 | import com.qifan.powerpermission.data.Configuration
20 | import com.qifan.powerpermission.data.DefaultConfiguration
21 | import com.qifan.powerpermission.data.PermissionResult
22 | import kotlin.properties.Delegates.notNull
23 |
24 | typealias Permission = String
25 | typealias RequestCode = Int
26 | typealias PermissionCallback = (PermissionResult) -> Unit
27 |
28 | /**
29 | * Declaration PowerPermission do initial work
30 | */
31 | object PowerPermission {
32 | internal var configuration: Configuration by notNull()
33 |
34 | /**
35 | * Init PowerPermission to make everything prepare to work.
36 | * @param configuration global configuration set custom settings in PowerPermission
37 | */
38 | fun init(configuration: Configuration = DefaultConfiguration()): PowerPermissionManager {
39 | this.configuration = configuration
40 | return PowerPermissionManager()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/core/PermissionFragment.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.core
17 |
18 | import android.content.Context
19 | import androidx.fragment.app.Fragment
20 | import com.qifan.powerpermission.core.extension.debug
21 | import com.qifan.powerpermission.core.extension.transact
22 | import com.qifan.powerpermission.data.PermissionResult
23 | import kotlin.properties.Delegates.notNull
24 |
25 | /**
26 | * Permission Fragment implemented silently into application for requesting permissions
27 | */
28 | class PermissionFragment : Fragment() {
29 | private var permissionParams: PermissionParams by notNull()
30 |
31 | override fun onAttach(context: Context) {
32 | super.onAttach(context)
33 | debug("onAttach(%s)", context)
34 | }
35 |
36 | override fun onDetach() {
37 | debug("onDetach()")
38 | super.onDetach()
39 | }
40 |
41 | internal fun askedPermission(params: PermissionParams) {
42 | val (permissions, code, _) = params
43 | debug("askedPermission(%s)", permissions.toString())
44 | requestPermissions(permissions.toTypedArray(), code)
45 | permissionParams = params
46 | }
47 |
48 | internal fun release() {
49 | if (parentFragment != null) {
50 | debug("Detaching PermissionFragment from parent Fragment %s", parentFragment)
51 | parentFragment?.transact {
52 | detach(this@PermissionFragment)
53 | remove(this@PermissionFragment)
54 | }
55 | } else if (activity != null) {
56 | debug("Detaching PermissionFragment from Activity %s", activity)
57 | activity?.transact {
58 | detach(this@PermissionFragment)
59 | remove(this@PermissionFragment)
60 | }
61 | }
62 | }
63 |
64 | override fun onRequestPermissionsResult(
65 | requestCode: Int,
66 | permissions: Array,
67 | grantResults: IntArray
68 | ) {
69 | val (_, code, callback, rationalHandler) = permissionParams
70 | if (requestCode == code) {
71 | val result = PermissionResult(
72 | fragment = this@PermissionFragment,
73 | permissions = permissions.toSet(),
74 | grantResults = grantResults
75 | )
76 | if (rationalHandler != null && rationalHandler.data.shouldInvokeRational(
77 | this,
78 | result
79 | )
80 | ) {
81 | rationalHandler.showRationale(::requestPermissions, code)
82 | } else {
83 | callback(result)
84 | }
85 | } else {
86 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/core/PermissionRequestParams.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.core
17 |
18 | import com.qifan.powerpermission.Permission
19 | import com.qifan.powerpermission.PermissionCallback
20 | import com.qifan.powerpermission.RequestCode
21 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
22 |
23 | /**
24 | * Default Permission request code
25 | */
26 | const val PERMISSION_REQUEST_CODE = 1
27 |
28 | internal typealias CleanCallback = () -> Unit
29 |
30 | internal data class PermissionParams(
31 | val permissions: List,
32 | val requestCode: RequestCode,
33 | val callback: PermissionCallback,
34 | val rationaleDelegate: RationaleDelegate?,
35 | val cleanCallback: CleanCallback
36 | )
37 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/core/PowerPermissionManager.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.core
17 |
18 | import androidx.annotation.VisibleForTesting
19 | import androidx.fragment.app.Fragment
20 | import androidx.fragment.app.FragmentActivity
21 | import com.qifan.powerpermission.Permission
22 | import com.qifan.powerpermission.PermissionCallback
23 | import com.qifan.powerpermission.RequestCode
24 | import com.qifan.powerpermission.core.extension.debug
25 | import com.qifan.powerpermission.core.extension.transact
26 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
27 |
28 | /**
29 | * internal manager to help dealing with runtime permissions
30 | */
31 | class PowerPermissionManager internal constructor() {
32 |
33 | private var permissionFragment: PermissionFragment? = null
34 |
35 | companion object {
36 | @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
37 | internal const val TAG_ACTIVITY = "[power_permission_fragment/activity]"
38 |
39 | @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
40 | internal const val TAG_FRAGMENT =
41 | "[power_permission_fragment/com.qifan.powerpermission.fragment]"
42 | }
43 |
44 | private fun onAttachPermissionFragment(fragmentActivity: FragmentActivity): PermissionFragment {
45 | permissionFragment = if (permissionFragment == null) {
46 | PermissionFragment().apply {
47 | debug(
48 | "Created new PermissionFragment for %s",
49 | fragmentActivity::class.java.simpleName
50 | )
51 | fragmentActivity.transact {
52 | add(this@apply, TAG_ACTIVITY)
53 | }
54 | }
55 | } else {
56 | debug(
57 | "Re-using PermissionFragment for %s",
58 | fragmentActivity::class.java.simpleName
59 | )
60 | permissionFragment
61 | }
62 | return permissionFragment ?: error("impossible!")
63 | }
64 |
65 | private fun onAttachPermissionFragment(fragment: Fragment): PermissionFragment {
66 | permissionFragment = if (permissionFragment == null) {
67 | PermissionFragment().apply {
68 | debug(
69 | "Created new PermissionFragment for %s",
70 | fragment::class.java.simpleName
71 | )
72 | fragment.transact {
73 | add(this@apply, TAG_FRAGMENT)
74 | }
75 | }
76 | } else {
77 | debug(
78 | "Re-using PermissionFragment for %s",
79 | fragment::class.java.simpleName
80 | )
81 | permissionFragment
82 | }
83 | return permissionFragment ?: error("impossible!")
84 | }
85 |
86 | private fun onDetachFragment() {
87 | permissionFragment?.release()
88 | permissionFragment = null
89 | }
90 |
91 | /**
92 | * All permission need to be requested
93 | * @param context context activity
94 | * @param permissions a list of permissions to be requested
95 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
96 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
97 | * @param callback to return after execute requesting permissions
98 | */
99 | fun requestPermissions(
100 | context: FragmentActivity,
101 | vararg permissions: Permission,
102 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
103 | rationaleDelegate: RationaleDelegate? = null,
104 | callback: PermissionCallback
105 | ) {
106 | check(permissions.isNotEmpty()) { "PowerPermission requires at least one input permission" }
107 | PermissionParams(
108 | permissions = permissions.toList(),
109 | requestCode = requestCode,
110 | callback = callback,
111 | rationaleDelegate = rationaleDelegate,
112 | cleanCallback = ::onDetachFragment
113 | ).apply {
114 | onAttachPermissionFragment(context).askedPermission(this)
115 | }
116 | }
117 |
118 | /**
119 | * All permission need to be requested
120 | * @param context context fragment
121 | * @param permissions a list of permissions to be requested
122 | * @param requestCode request Permission CODE by default is [PERMISSION_REQUEST_CODE]
123 | * @param rationaleDelegate rationaleHandler to handle displaying reason interaction
124 | * @param callback to return after execute requesting permissions
125 | */
126 | fun requestPermissions(
127 | context: Fragment,
128 | vararg permissions: Permission,
129 | requestCode: RequestCode = PERMISSION_REQUEST_CODE,
130 | rationaleDelegate: RationaleDelegate? = null,
131 | callback: PermissionCallback
132 | ) {
133 | check(permissions.isNotEmpty()) { "PowerPermission requires at least one input permission" }
134 | PermissionParams(
135 | permissions = permissions.toList(),
136 | requestCode = requestCode,
137 | callback = callback,
138 | rationaleDelegate = rationaleDelegate,
139 | cleanCallback = ::onDetachFragment
140 | ).apply {
141 | onAttachPermissionFragment(context).askedPermission(this)
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/core/extension/FragmentExt.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.core.extension
17 |
18 | import android.content.Context
19 | import androidx.fragment.app.Fragment
20 | import androidx.fragment.app.FragmentActivity
21 | import androidx.fragment.app.FragmentTransaction
22 |
23 | /**
24 | * add com.qifan.powerpermission.fragment transaction
25 | * @param action dsl [FragmentTransaction]
26 | * @return Returns true if there were any pending transactions to be executed
27 | */
28 | internal fun FragmentActivity.transact(action: FragmentTransaction.() -> Unit) =
29 | supportFragmentManager.let {
30 | it.beginTransaction()
31 | .apply {
32 | action()
33 | commit()
34 | }
35 | it.executePendingTransactions()
36 | }
37 |
38 | /**
39 | * add com.qifan.powerpermission.fragment transaction
40 | * @param action dsl [FragmentTransaction]
41 | * @return Returns true if there were any pending transactions to be executed
42 | */
43 | internal fun Fragment.transact(action: FragmentTransaction.(Context) -> Unit) {
44 | childFragmentManager.beginTransaction()
45 | .apply {
46 | action(activity ?: error("Fragment's activity is null."))
47 | commit()
48 | }
49 | childFragmentManager.executePendingTransactions()
50 | }
51 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/core/extension/LoggingExt.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.core.extension
17 |
18 | import android.util.Log
19 | import com.qifan.powerpermission.PowerPermission
20 |
21 | /**
22 | * Helper function to do the log debug work
23 | * @param message basic message
24 | * @param args additional message
25 | */
26 | internal fun Any.debug(
27 | message: String,
28 | vararg args: Any?
29 | ) {
30 | if (PowerPermission.configuration.enableLog) {
31 | try {
32 | Log.d(this::class.java.simpleName, message.format(*args))
33 | } catch (_: Exception) {
34 | }
35 | }
36 | }
37 |
38 | /**
39 | * Helper function to do the log warn work
40 | * @param message basic message
41 | * @param args additional message
42 | */
43 | internal fun Any.warn(
44 | message: String,
45 | vararg args: Any?
46 | ) {
47 | if (PowerPermission.configuration.enableLog) {
48 | try {
49 | Log.w(this::class.java.simpleName, message.format(*args))
50 | } catch (_: Exception) {
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/core/extension/PermissionExt.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.core.extension
17 |
18 | import android.content.Context
19 | import android.content.pm.PackageManager
20 | import androidx.annotation.CheckResult
21 | import androidx.core.content.ContextCompat
22 | import androidx.fragment.app.Fragment
23 | import com.qifan.powerpermission.Permission
24 | import com.qifan.powerpermission.data.GrantResult
25 | import com.qifan.powerpermission.data.PermissionData
26 |
27 | /**
28 | * helper function to check all permissions are granted or not.
29 | * @param permissions list of permissions e.g.[android.Manifest.permission.CALL_PHONE]
30 | * @return `true` if ALL given [permissions] have been granted.
31 | * */
32 | @CheckResult
33 | internal fun Context.isAllGranted(vararg permissions: Permission): Boolean {
34 | return permissions.all {
35 | ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
36 | }
37 | }
38 |
39 | /** @return `true` if given permission were granted. */
40 | @CheckResult
41 | internal fun PermissionData.isGranted(): Boolean {
42 | return grantResult == GrantResult.GRANTED
43 | }
44 |
45 | /** @return `true` if given permission were rational. */
46 | @CheckResult
47 | internal fun PermissionData.isRational(): Boolean {
48 | return grantResult == GrantResult.RATIONAL
49 | }
50 |
51 | /** @return `true` if given permission were permanent denied. */
52 | @CheckResult
53 | internal fun PermissionData.isPermanentDenied(): Boolean {
54 | return grantResult == GrantResult.PERMANENTLY_DENIED
55 | }
56 |
57 | /** @return `true` if given permissions contains rationale permission. */
58 | @CheckResult
59 | internal fun Fragment.isRational(permissions: List): Boolean {
60 | return permissions.any { shouldShowRequestPermissionRationale(it) }
61 | }
62 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/data/Configuration.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.data
17 |
18 | /**
19 | * configuration file to set global environment
20 | */
21 | interface Configuration {
22 | /**
23 | * enable log mode or not
24 | */
25 | val enableLog: Boolean
26 | }
27 |
28 | /**
29 | * Default Implementation of configuration for setting global environment
30 | */
31 | data class DefaultConfiguration(
32 | override val enableLog: Boolean = false
33 | ) : Configuration
34 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/data/GrantResult.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.data
17 |
18 | import android.content.pm.PackageManager
19 | import com.qifan.powerpermission.Permission
20 | import com.qifan.powerpermission.core.PermissionFragment
21 | import com.qifan.powerpermission.core.extension.debug
22 |
23 | /**
24 | * enum grant result to represent status of permission grant
25 | */
26 | enum class GrantResult {
27 | GRANTED,
28 | RATIONAL,
29 | PERMANENTLY_DENIED
30 | }
31 |
32 | internal fun PermissionFragment.asGrantResult(
33 | grantResult: Int,
34 | permission: Permission
35 | ): GrantResult {
36 | return when (grantResult) {
37 | PackageManager.PERMISSION_GRANTED -> GrantResult.GRANTED
38 | else -> {
39 | if (shouldShowRequestPermissionRationale(permission)) {
40 | debug("what the fuck $permission")
41 | GrantResult.RATIONAL
42 | } else {
43 | GrantResult.PERMANENTLY_DENIED
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/data/PermissionResult.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.data
17 |
18 | import androidx.annotation.CheckResult
19 | import com.qifan.powerpermission.Permission
20 | import com.qifan.powerpermission.core.PermissionFragment
21 | import com.qifan.powerpermission.core.extension.isGranted
22 | import com.qifan.powerpermission.core.extension.isPermanentDenied
23 | import com.qifan.powerpermission.core.extension.isRational
24 |
25 | /**
26 | * permission result wrapped callback essential data
27 | * @param resultsSet set of permissions
28 | */
29 | data class PermissionResult internal constructor(
30 | internal val resultsSet: Set
31 | ) {
32 | internal constructor(
33 | fragment: PermissionFragment,
34 | permissions: Set,
35 | grantResults: IntArray
36 | ) : this(
37 | permissions.mapIndexed { index, permission ->
38 | val grantResult: Int = grantResults[index]
39 | PermissionData(
40 | data = permission,
41 | grantResult = fragment.asGrantResult(grantResult, permission)
42 | )
43 | }
44 | .toSet()
45 | )
46 | }
47 |
48 | /**
49 | * Wrapped essential permission data information
50 | * @param data simple declaration permission such as [android.Manifest.permission.CAMERA].
51 | * @param grantResult [GrantResult] such as [GrantResult.GRANTED] etc.
52 | */
53 | data class PermissionData(
54 | val data: Permission,
55 | val grantResult: GrantResult
56 | ) {
57 | override fun hashCode(): Int {
58 | return data.hashCode()
59 | }
60 |
61 | override fun equals(other: Any?): Boolean {
62 | return other != null &&
63 | other is PermissionData &&
64 | this.data == other.data
65 | }
66 | }
67 |
68 | /** @return `true` if given permissions were granted. */
69 | @CheckResult
70 | fun PermissionResult.hasAllGranted(): Boolean {
71 | return resultsSet
72 | .all { it.isGranted() }
73 | }
74 |
75 | /** @return `true` if given permissions have rational permissions. */
76 | @CheckResult
77 | fun PermissionResult.hasRational(): Boolean {
78 | return resultsSet
79 | .any { it.isRational() }
80 | }
81 |
82 | /** @return `true` if given permissions have permanent denied permissions. */
83 | @CheckResult
84 | fun PermissionResult.hasPermanentDenied(): Boolean {
85 | return resultsSet
86 | .any { it.isPermanentDenied() }
87 | }
88 |
89 | /** @return a list of all required permissions. */
90 | @CheckResult
91 | fun PermissionResult.requiredPermissions(): List {
92 | return resultsSet
93 | .asSequence()
94 | .map { it.data }
95 | .toList()
96 | }
97 |
98 | /** @return a list of all granted permissions. */
99 | @CheckResult
100 | fun PermissionResult.granted(): List {
101 | return resultsSet.filter { it.isGranted() }
102 | .map { it.data }
103 | }
104 |
105 | /** @return a list of all rational permissions. */
106 | @CheckResult
107 | fun PermissionResult.rational(): List {
108 | return resultsSet.filter { it.isRational() }
109 | .map { it.data }
110 | }
111 |
112 | /** @return a list of all denied permissions. */
113 | @CheckResult
114 | fun PermissionResult.permanentDenied(): List {
115 | return resultsSet.filter { it.isPermanentDenied() }
116 | .map { it.data }
117 | }
118 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/data/RationaleData.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.data
17 |
18 | import androidx.annotation.CheckResult
19 | import androidx.fragment.app.Fragment
20 | import com.qifan.powerpermission.Permission
21 | import com.qifan.powerpermission.core.extension.isRational
22 |
23 | /**
24 | * Class for set value for Rationale permissions
25 | * @param rationalPermission target permission to display reason
26 | * e.g [android.Manifest.permission.ACCESS_COARSE_LOCATION]
27 | * @param rationalPermissions target list of permissions to display reason
28 | * e.g [listOf] [android.Manifest.permission.ACCESS_COARSE_LOCATION])
29 | * @param message display reason about requiring permission
30 | */
31 | data class RationaleData internal constructor(
32 | private val rationalPermission: Permission?,
33 | private val rationalPermissions: List?,
34 | val message: String
35 | ) {
36 | constructor(
37 | rationalPermission: Permission,
38 | message: String
39 | ) : this(rationalPermission, null, message)
40 |
41 | constructor(
42 | rationalPermissions: List,
43 | message: String
44 | ) : this(null, rationalPermissions, message)
45 |
46 | internal fun getRationalePermission(): List {
47 | return if (rationalPermissions.isNullOrEmpty()) {
48 | listOf(rationalPermission!!)
49 | } else {
50 | rationalPermissions
51 | }
52 | }
53 |
54 | /** @return `true` if given user permissions contains rational permissions. */
55 | @CheckResult
56 | internal fun shouldInvokeRational(
57 | fragment: Fragment,
58 | permissionResult: PermissionResult
59 | ): Boolean {
60 | return permissionResult.hasRational() && fragment.isRational(getRationalePermission())
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/rationale/DialogRationale.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rationale
17 |
18 | import android.app.Activity
19 | import androidx.annotation.StringRes
20 | import androidx.fragment.app.Fragment
21 | import com.qifan.powerpermission.Permission
22 | import com.qifan.powerpermission.data.RationaleData
23 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
24 | import com.qifan.powerpermission.rationale.delegate.dialog.DialogRationaleDelegate
25 |
26 | /**
27 | * shortcut extension to display dialog rationale explains required permission
28 | * @param dialogTitle title of dialog
29 | * @param requiredPermission target permission when call [Fragment.shouldShowRequestPermissionRationale]
30 | * return true to display dialog
31 | * @param message to describe reason about requiring permission
32 | * @param positiveText to set string to [android.app.AlertDialog.BUTTON_POSITIVE]
33 | * @param negativeText to set string to [android.app.AlertDialog.BUTTON_NEGATIVE]
34 | */
35 | fun Fragment.createDialogRationale(
36 | @StringRes dialogTitle: Int,
37 | requiredPermission: Permission,
38 | message: String,
39 | positiveText: String = getString(android.R.string.ok),
40 | negativeText: String? = null
41 | ): RationaleDelegate {
42 | return with(
43 | RationaleData(
44 | rationalPermission = requiredPermission,
45 | message = message
46 | )
47 | ) {
48 | DialogRationaleDelegate(
49 | context = requireActivity(),
50 | dialogTitle = dialogTitle,
51 | data = this,
52 | positiveText = positiveText,
53 | negativeText = negativeText
54 | )
55 | }
56 | }
57 |
58 | /**
59 | * shortcut extension to display dialog rationale explains required permission
60 | * @param dialogTitle title of dialog
61 | * @param requiredPermissions list of target permission when call
62 | * [Fragment.shouldShowRequestPermissionRationale] return true to display dialog
63 | * @param message to describe reason about requiring permission
64 | * @param positiveText to set string to [android.app.AlertDialog.BUTTON_POSITIVE]
65 | * @param negativeText to set string to [android.app.AlertDialog.BUTTON_NEGATIVE]
66 | */
67 | fun Fragment.createDialogRationale(
68 | @StringRes dialogTitle: Int,
69 | requiredPermissions: List,
70 | message: String,
71 | positiveText: String = getString(android.R.string.ok),
72 | negativeText: String? = null
73 | ): RationaleDelegate {
74 | return with(
75 | RationaleData(
76 | rationalPermissions = requiredPermissions,
77 | message = message
78 | )
79 | ) {
80 | DialogRationaleDelegate(
81 | context = requireActivity(),
82 | dialogTitle = dialogTitle,
83 | data = this,
84 | positiveText = positiveText,
85 | negativeText = negativeText
86 | )
87 | }
88 | }
89 |
90 | /**
91 | * shortcut extension to display dialog rationale explains required permission
92 | * @param dialogTitle title of dialog
93 | * @param requiredPermission target permission when call [Fragment.shouldShowRequestPermissionRationale]
94 | * return true to display dialog
95 | * @param message to describe reason about requiring permission
96 | * @param positiveText to set string to [android.app.AlertDialog.BUTTON_POSITIVE]
97 | * @param negativeText to set string to [android.app.AlertDialog.BUTTON_NEGATIVE]
98 | */
99 | fun Activity.createDialogRationale(
100 | @StringRes dialogTitle: Int,
101 | requiredPermission: Permission,
102 | message: String,
103 | positiveText: String = getString(android.R.string.ok),
104 | negativeText: String? = null
105 | ): RationaleDelegate {
106 | return with(
107 | RationaleData(
108 | rationalPermission = requiredPermission,
109 | message = message
110 | )
111 | ) {
112 | DialogRationaleDelegate(
113 | context = this@createDialogRationale,
114 | dialogTitle = dialogTitle,
115 | data = this,
116 | positiveText = positiveText,
117 | negativeText = negativeText
118 | )
119 | }
120 | }
121 |
122 | /**
123 | * shortcut extension to display dialog rationale explains required permission
124 | * @param dialogTitle title of dialog
125 | * @param requiredPermissions list of target permission when call
126 | * [Fragment.shouldShowRequestPermissionRationale] return true to display dialog
127 | * @param message to describe reason about requiring permission
128 | * @param positiveText to set string to [android.app.AlertDialog.BUTTON_POSITIVE]
129 | * @param negativeText to set string to [android.app.AlertDialog.BUTTON_NEGATIVE]
130 | */
131 | fun Activity.createDialogRationale(
132 | @StringRes dialogTitle: Int,
133 | requiredPermissions: List,
134 | message: String,
135 | positiveText: String = getString(android.R.string.ok),
136 | negativeText: String? = null
137 | ): RationaleDelegate {
138 | return with(
139 | RationaleData(
140 | rationalPermissions = requiredPermissions,
141 | message = message
142 | )
143 | ) {
144 | DialogRationaleDelegate(
145 | context = this@createDialogRationale,
146 | dialogTitle = dialogTitle,
147 | data = this,
148 | positiveText = positiveText,
149 | negativeText = negativeText
150 | )
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/rationale/delegate/RationaleActionCallback.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rationale.delegate
17 |
18 | import com.qifan.powerpermission.core.extension.warn
19 |
20 | class RationaleActionCallback internal constructor(
21 | private var action: ((recheck: Boolean) -> Unit)?
22 | ) {
23 | operator fun invoke(recheck: Boolean) {
24 | if (action == null) {
25 | warn("Confirm callback invoked more than once, ignored after first invocation.")
26 | }
27 | action?.invoke(recheck)
28 | action = null
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/rationale/delegate/RationaleDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rationale.delegate
17 |
18 | import com.qifan.powerpermission.Permission
19 | import com.qifan.powerpermission.RequestCode
20 | import com.qifan.powerpermission.core.extension.debug
21 | import com.qifan.powerpermission.data.RationaleData
22 |
23 | internal typealias PermissionRequest = (Array, RequestCode) -> Unit
24 |
25 | /**
26 | * interface to delegate [RuntimePermission] decide which way to display rationale view
27 | */
28 | interface RationaleDelegate {
29 | // essential data about rational data
30 | val data: RationaleData
31 |
32 | /**
33 | * function to invoke showing a rational view displaying when permissions have been refused
34 | * @param request callback to invoke request permission in PermissionFragment
35 | * @param requestCode request Permission CODE
36 | */
37 | fun showRationale(request: PermissionRequest, requestCode: RequestCode) {
38 | displayRationale(
39 | message = data.message,
40 | actionCallback = RationaleActionCallback { recheck ->
41 | if (recheck) {
42 | onAcceptRecheckPermission(request, requestCode)
43 | } else {
44 | onRefuseRecheckPermission()
45 | }
46 | },
47 | permission = data
48 | .getRationalePermission()
49 | .toTypedArray()
50 | )
51 | }
52 |
53 | /**
54 | * display a view to explain reason to user why request permissions
55 | * @param permission target rational permission or permissions
56 | * @param message reason about explaining why request permissions
57 | * @param actionCallback invoke to recheck permission or not when give `true` it will trigger
58 | * [onAcceptRecheckPermission] otherwise [onRefuseRecheckPermission]
59 | */
60 | fun displayRationale(
61 | vararg permission: Permission,
62 | message: String,
63 | actionCallback: RationaleActionCallback
64 | )
65 |
66 | private fun onAcceptRecheckPermission(
67 | requestPermission: PermissionRequest,
68 | requestCode: RequestCode
69 | ) {
70 | debug("Permissions %s will been asked again", data.getRationalePermission())
71 | onDismissView()
72 | requestPermission(
73 | data
74 | .getRationalePermission()
75 | .toTypedArray(),
76 | requestCode
77 | )
78 | }
79 |
80 | private fun onRefuseRecheckPermission() {
81 | debug("Permissions %s are denied", data.getRationalePermission())
82 | onDismissView()
83 | }
84 |
85 | /**
86 | * rational view to disappear
87 | */
88 | fun onDismissView()
89 | }
90 |
--------------------------------------------------------------------------------
/powerpermission/src/main/java/com/qifan/powerpermission/rationale/delegate/dialog/DialogRationaleDelegate.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.rationale.delegate.dialog
17 |
18 | import android.app.AlertDialog
19 | import android.content.Context
20 | import androidx.annotation.StringRes
21 | import com.qifan.powerpermission.Permission
22 | import com.qifan.powerpermission.data.RationaleData
23 | import com.qifan.powerpermission.rationale.delegate.RationaleActionCallback
24 | import com.qifan.powerpermission.rationale.delegate.RationaleDelegate
25 |
26 | /**
27 | * Implementation Dialog of RationaleDelegate
28 | */
29 | class DialogRationaleDelegate internal constructor(
30 | private val context: Context,
31 | @StringRes private val dialogTitle: Int,
32 | private val positiveText: String,
33 | private val negativeText: String?,
34 | override val data: RationaleData
35 | ) : RationaleDelegate {
36 | private var dialog: AlertDialog? = null
37 |
38 | override fun displayRationale(
39 | vararg permission: Permission,
40 | message: String,
41 | actionCallback: RationaleActionCallback
42 | ) {
43 |
44 | dialog = with(AlertDialog.Builder(context)) {
45 | setTitle(dialogTitle)
46 | setMessage(message)
47 | setPositiveButton(positiveText) { dialog, _ ->
48 | (dialog as AlertDialog).setOnDismissListener(null)
49 | actionCallback(recheck = true)
50 | }
51 | negativeText?.let {
52 | setNegativeButton(it) { _, _ ->
53 | (dialog as AlertDialog).setOnDismissListener(null)
54 | actionCallback(recheck = false)
55 | }
56 | }
57 | setOnDismissListener {
58 | actionCallback(recheck = false)
59 | }
60 | setCancelable(negativeText.isNullOrBlank())
61 | show()
62 | }
63 | }
64 |
65 | override fun onDismissView() {
66 | dialog?.dismiss()
67 | dialog = null
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/powerpermission/src/test/java/com/qifan/powerpermission/PowerPermissionManager.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission
17 |
--------------------------------------------------------------------------------
/powerpermission/src/test/java/com/qifan/powerpermission/data/GrantResultTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.data
17 |
18 | import android.content.pm.PackageManager
19 | import com.qifan.powerpermission.PowerPermission
20 | import com.qifan.powerpermission.core.PermissionFragment
21 | import io.mockk.every
22 | import io.mockk.mockk
23 | import io.mockk.mockkObject
24 | import org.assertj.core.api.Assertions.assertThat
25 | import org.spekframework.spek2.Spek
26 |
27 | object GrantResultTest : Spek({
28 | group("Grant Result Extension Test") {
29 | val mockedFragment by memoized { mockk() }
30 | val mockedPermission by memoized { "mockedPermission" }
31 | test("on permission granted return GRANTED") {
32 | assertThat(
33 | mockedFragment.asGrantResult(
34 | grantResult = PackageManager.PERMISSION_GRANTED,
35 | permission = mockedPermission
36 | )
37 | ).isEqualTo(
38 | GrantResult.GRANTED
39 | )
40 | }
41 | test("on permission denied and not rational return PERMANENTLY_DENIED") {
42 | every {
43 | mockedFragment.shouldShowRequestPermissionRationale(mockedPermission)
44 | } returns false
45 | assertThat(
46 | mockedFragment.asGrantResult(
47 | grantResult = PackageManager.PERMISSION_DENIED,
48 | permission = mockedPermission
49 | )
50 | ).isEqualTo(
51 | GrantResult.PERMANENTLY_DENIED
52 | )
53 | }
54 |
55 | test("on permission denied and rational return RATIONAL") {
56 | mockkObject(PowerPermission)
57 | every {
58 | mockedFragment.shouldShowRequestPermissionRationale(mockedPermission)
59 | } returns true
60 | every { PowerPermission.configuration } returns DefaultConfiguration()
61 | assertThat(
62 | mockedFragment.asGrantResult(
63 | grantResult = PackageManager.PERMISSION_DENIED,
64 | permission = mockedPermission
65 | )
66 | ).isEqualTo(
67 | GrantResult.RATIONAL
68 | )
69 | }
70 | }
71 | })
72 |
--------------------------------------------------------------------------------
/powerpermission/src/test/java/com/qifan/powerpermission/data/PermissionResultTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.data
17 |
18 | import android.Manifest
19 | import android.content.pm.PackageManager
20 | import com.qifan.powerpermission.PowerPermission
21 | import com.qifan.powerpermission.core.PermissionFragment
22 | import io.mockk.every
23 | import io.mockk.mockk
24 | import io.mockk.mockkObject
25 | import org.assertj.core.api.Assertions.assertThat
26 | import org.spekframework.spek2.Spek
27 | import org.spekframework.spek2.style.specification.describe
28 |
29 | object PermissionResultTest : Spek({
30 | describe("Permission Result") {
31 | it("should invoke set constructor") {
32 | mockkObject(PowerPermission)
33 | every { PowerPermission.configuration } returns DefaultConfiguration()
34 | val mockedFragment = mockk()
35 | every {
36 | mockedFragment.shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE)
37 | } returns false
38 | every {
39 | mockedFragment.shouldShowRequestPermissionRationale(Manifest.permission.READ_CALENDAR)
40 | } returns true
41 | val result = PermissionResult(
42 | mockedFragment,
43 | setOf(
44 | Manifest.permission.CAMERA,
45 | Manifest.permission.CALL_PHONE,
46 | Manifest.permission.READ_CALENDAR
47 | ),
48 | intArrayOf(
49 | PackageManager.PERMISSION_GRANTED,
50 | PackageManager.PERMISSION_DENIED,
51 | PackageManager.PERMISSION_DENIED
52 | )
53 | )
54 | assertThat(result).isEqualTo(
55 | PermissionResult(
56 | setOf(
57 | PermissionData(
58 | data = Manifest.permission.CAMERA,
59 | grantResult = GrantResult.GRANTED
60 | ),
61 | PermissionData(
62 | data = Manifest.permission.CALL_PHONE,
63 | grantResult = GrantResult.PERMANENTLY_DENIED
64 | ),
65 | PermissionData(
66 | data = Manifest.permission.READ_CALENDAR,
67 | grantResult = GrantResult.RATIONAL
68 | )
69 | )
70 | )
71 | )
72 | }
73 | }
74 | })
75 |
--------------------------------------------------------------------------------
/powerpermission/src/test/java/com/qifan/powerpermission/data/RationaleDataTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2020 by Qifan YANG (@underwindfall)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.qifan.powerpermission.data
17 |
18 | import org.assertj.core.api.Assertions.assertThat
19 | import org.spekframework.spek2.Spek
20 |
21 | object RationaleDataTest : Spek({
22 | group("Rationale Data") {
23 | val mockedPermission by memoized { "mockedPermission" }
24 | val mockedPermissions by memoized { listOf("mockedPermission", "mockedPermission2") }
25 | val mockedMessage by memoized { "mockedMessage" }
26 | group("RationaleData constructor") {
27 | test("Invoke permission message constructor") {
28 | assertThat(
29 | RationaleData(
30 | rationalPermission = mockedPermission,
31 | message = mockedMessage
32 | )
33 | ).isEqualTo(
34 | RationaleData(
35 | rationalPermission = "mockedPermission",
36 | rationalPermissions = null,
37 | message = "mockedMessage"
38 | )
39 | )
40 | }
41 |
42 | test("Invoke permissions message constructor") {
43 | assertThat(
44 | RationaleData(
45 | rationalPermissions = mockedPermissions,
46 | message = mockedMessage
47 | )
48 | ).isEqualTo(
49 | RationaleData(
50 | rationalPermission = null,
51 | rationalPermissions = listOf("mockedPermission", "mockedPermission2"),
52 | message = "mockedMessage"
53 | )
54 | )
55 | }
56 | }
57 | }
58 | })
59 |
--------------------------------------------------------------------------------
/publish-module.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 | apply plugin: 'signing'
3 |
4 | // build a jar with source files
5 | task sourcesJar(type: Jar) {
6 | from android.sourceSets.main.java.srcDirs
7 | archiveClassifier.set('sources')
8 | }
9 |
10 | task javadoc(type: Javadoc) {
11 | failOnError false
12 | source = android.sourceSets.main.java.sourceFiles
13 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
14 | // classpath += configurations.compile
15 | }
16 |
17 | // build a jar with javadoc
18 | task javadocJar(type: Jar, dependsOn: javadoc) {
19 | archiveClassifier.set('javadoc')
20 | from javadoc.destinationDir
21 | }
22 |
23 |
24 | task androidSourcesJar(type: Jar) {
25 | archiveClassifier.set('sources')
26 | if (project.plugins.findPlugin("com.android.library")) {
27 | // For Android libraries
28 | from android.sourceSets.main.java.srcDirs
29 | from android.sourceSets.main.kotlin.srcDirs
30 | } else {
31 | // For pure Kotlin libraries, in case you have them
32 | from sourceSets.main.java.srcDirs
33 | from sourceSets.main.kotlin.srcDirs
34 | }
35 | }
36 |
37 | artifacts {
38 | archives sourcesJar
39 | archives javadocJar
40 | archives androidSourcesJar
41 | }
42 |
43 | group = PUBLISH_GROUP_ID
44 | version = PUBLISH_VERSION
45 |
46 | afterEvaluate {
47 | publishing {
48 | publications {
49 | release(MavenPublication) {
50 | // The coordinates of the library, being set from variables that
51 | // we'll set up later
52 | groupId PUBLISH_GROUP_ID
53 | artifactId PUBLISH_ARTIFACT_ID
54 | version PUBLISH_VERSION
55 |
56 | // Two artifacts, the `aar` (or `jar`) and the sources
57 | if (project.plugins.findPlugin("com.android.library")) {
58 | from components.release
59 | } else {
60 | from components.java
61 | }
62 |
63 | artifact androidSourcesJar
64 | artifact javadocJar
65 |
66 | // Mostly self-explanatory metadata
67 | pom {
68 | name = PUBLISH_ARTIFACT_ID
69 | description = "PowerPermission makes handling runtime permissions extremely easy."
70 | url = "https://github.com/underwindfall/PowerPermission"
71 | licenses {
72 | license {
73 | name = 'Apache License'
74 | url = 'https://github.com/underwindfall/PowerPermission/blob/master/LICENSE'
75 | }
76 | }
77 | developers {
78 | developer {
79 | id = 'underwindfall'
80 | name = 'Qifan YANG'
81 | }
82 | }
83 |
84 | scm {
85 | connection = 'scm:git:github.com/underwindfall/PowerPermission.git'
86 | developerConnection = 'scm:git:ssh://github.com/underwindfall/PowerPermission.git'
87 | url = 'https://github.com/underwindfall/PowerPermission/tree/main'
88 | }
89 | }
90 | }
91 | }
92 | }
93 | }
94 |
95 | signing {
96 | useInMemoryPgpKeys(
97 | rootProject.ext["signing.keyId"],
98 | rootProject.ext["signing.key"],
99 | rootProject.ext["signing.password"],
100 | )
101 | sign publishing.publications
102 | }
103 |
--------------------------------------------------------------------------------
/publish-root.gradle:
--------------------------------------------------------------------------------
1 | ext["ossrhUsername"] = ''
2 | ext["ossrhPassword"] = ''
3 | ext["sonatypeStagingProfileId"] = ''
4 | ext["signing.keyId"] = ''
5 | ext["signing.password"] = ''
6 | ext["signing.key"] = ''
7 |
8 | File secretPropsFile = project.rootProject.file('local.properties')
9 | if (secretPropsFile.exists()) {
10 | // Read local.properties file first if it exists
11 | Properties p = new Properties()
12 | new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
13 | p.each { name, value -> ext[name] = value}
14 |
15 | }
16 | // Use system environment variables
17 | ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') ?: ext["ossrhUsername"]
18 | ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') ?: ext["ossrhPassword"]
19 | ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') ?: ext["sonatypeStagingProfileId"]
20 | ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') ?: ext["signing.keyId"]
21 | ext["signing.password"] = System.getenv('SIGNING_PASSWORD') ?: ext["signing.password"]
22 | ext["signing.key"] = System.getenv('SIGNING_KEY') ?: ext["signing.key"]
23 |
24 | // Set up Sonatype repository
25 | nexusPublishing {
26 | repositories {
27 | sonatype {
28 | stagingProfileId = sonatypeStagingProfileId
29 | username = ossrhUsername
30 | password = ossrhPassword
31 | // Add these lines if using new Sonatype infra
32 | nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
33 | snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = "PowerPermission"
2 | include(
3 | ":app",
4 | ":powerpermission",
5 | ":powerpermission-rxjava2",
6 | ":powerpermission-rxjava3",
7 | ":powerpermission-coroutines",
8 | ":powerpermission-livedata"
9 | )
10 |
--------------------------------------------------------------------------------
/spotless/copyright.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) $YEAR by Qifan YANG (@underwindfall)
3 | *
4 | *
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 | * except in compliance with the License. You may obtain a copy of the License at
6 | *
7 | *