Repository: yanzhenjie/AndPermission Branch: master Commit: 00bedd6a3a93 Files: 143 Total size: 323.5 KB Directory structure: gitextract_hhydg_6p/ ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle ├── config.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── permission/ │ ├── .gitignore │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── aidl/ │ │ └── com/ │ │ └── yanzhenjie/ │ │ └── permission/ │ │ └── bridge/ │ │ └── IBridge.aidl │ ├── java/ │ │ └── com/ │ │ └── yanzhenjie/ │ │ └── permission/ │ │ ├── Action.java │ │ ├── AndPermission.java │ │ ├── Boot.java │ │ ├── FileProvider.java │ │ ├── Rationale.java │ │ ├── RequestExecutor.java │ │ ├── bridge/ │ │ │ ├── BridgeActivity.java │ │ │ ├── BridgeRequest.java │ │ │ ├── BridgeService.java │ │ │ ├── Messenger.java │ │ │ ├── RequestExecutor.java │ │ │ └── RequestManager.java │ │ ├── checker/ │ │ │ ├── CalendarReadTest.java │ │ │ ├── CalendarWriteTest.java │ │ │ ├── CallLogReadTest.java │ │ │ ├── CallLogWriteTest.java │ │ │ ├── CameraTest.java │ │ │ ├── ContactsReadTest.java │ │ │ ├── ContactsWriteTest.java │ │ │ ├── DoubleChecker.java │ │ │ ├── LocationCoarseTest.java │ │ │ ├── LocationFineTest.java │ │ │ ├── PermissionChecker.java │ │ │ ├── PermissionTest.java │ │ │ ├── PhoneStateReadTest.java │ │ │ ├── RecordAudioTest.java │ │ │ ├── SensorActivityTest.java │ │ │ ├── SensorHeartTest.java │ │ │ ├── SipTest.java │ │ │ ├── SmsReadTest.java │ │ │ ├── StandardChecker.java │ │ │ ├── StorageReadTest.java │ │ │ ├── StorageWriteTest.java │ │ │ └── StrictChecker.java │ │ ├── install/ │ │ │ ├── BaseRequest.java │ │ │ ├── InstallRequest.java │ │ │ ├── NRequest.java │ │ │ ├── NRequestFactory.java │ │ │ ├── ORequest.java │ │ │ └── ORequestFactory.java │ │ ├── notify/ │ │ │ ├── BaseRequest.java │ │ │ ├── NRequest.java │ │ │ ├── NRequestFactory.java │ │ │ ├── Notify.java │ │ │ ├── ORequest.java │ │ │ ├── ORequestFactory.java │ │ │ ├── PermissionRequest.java │ │ │ ├── listener/ │ │ │ │ ├── BaseRequest.java │ │ │ │ ├── J1Request.java │ │ │ │ ├── J1RequestFactory.java │ │ │ │ ├── J2Request.java │ │ │ │ ├── J2RequestFactory.java │ │ │ │ └── ListenerRequest.java │ │ │ └── option/ │ │ │ └── NotifyOption.java │ │ ├── option/ │ │ │ └── Option.java │ │ ├── overlay/ │ │ │ ├── BaseRequest.java │ │ │ ├── LRequest.java │ │ │ ├── LRequestFactory.java │ │ │ ├── MRequest.java │ │ │ ├── MRequestFactory.java │ │ │ ├── OverlayRequest.java │ │ │ └── setting/ │ │ │ ├── LSettingPage.java │ │ │ └── MSettingPage.java │ │ ├── runtime/ │ │ │ ├── BaseRequest.java │ │ │ ├── LRequest.java │ │ │ ├── LRequestFactory.java │ │ │ ├── MRequest.java │ │ │ ├── MRequestFactory.java │ │ │ ├── Permission.java │ │ │ ├── PermissionDef.java │ │ │ ├── PermissionRequest.java │ │ │ ├── Runtime.java │ │ │ ├── option/ │ │ │ │ └── RuntimeOption.java │ │ │ └── setting/ │ │ │ ├── AllRequest.java │ │ │ ├── SettingPage.java │ │ │ └── SettingRequest.java │ │ ├── setting/ │ │ │ ├── Setting.java │ │ │ └── write/ │ │ │ ├── BaseRequest.java │ │ │ ├── LWriteRequest.java │ │ │ ├── LWriteRequestFactory.java │ │ │ ├── MWriteRequest.java │ │ │ ├── MWriteRequestFactory.java │ │ │ └── WriteRequest.java │ │ ├── source/ │ │ │ ├── ActivitySource.java │ │ │ ├── ContextSource.java │ │ │ ├── FragmentSource.java │ │ │ ├── Source.java │ │ │ ├── WrapperSource.java │ │ │ └── XFragmentSource.java │ │ ├── task/ │ │ │ ├── TaskExecutor.java │ │ │ └── WaitDialog.java │ │ └── util/ │ │ └── StringUtils.java │ └── res/ │ ├── drawable/ │ │ └── permission_shape_wait_background.xml │ ├── layout/ │ │ └── permission_dialog_wait.xml │ ├── values/ │ │ ├── dimens.xml │ │ ├── string.xml │ │ └── style.xml │ ├── values-v21/ │ │ └── style.xml │ ├── values-zh/ │ │ └── string.xml │ ├── values-zh-RHK/ │ │ └── string.xml │ ├── values-zh-RTW/ │ │ └── string.xml │ └── xml/ │ └── permission_file_paths.xml ├── sample/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── assets/ │ │ └── android.apk │ ├── java/ │ │ └── com/ │ │ └── yanzhenjie/ │ │ └── permission/ │ │ └── sample/ │ │ ├── App.java │ │ ├── InstallRationale.java │ │ ├── NotifyListenerRationale.java │ │ ├── NotifyRationale.java │ │ ├── OverlayRationale.java │ │ ├── RuntimeRationale.java │ │ ├── WriteSettingRationale.java │ │ ├── app/ │ │ │ ├── MainActivity.java │ │ │ └── NotifyListenerService.java │ │ ├── util/ │ │ │ ├── FileUtils.java │ │ │ └── IOUtils.java │ │ └── widget/ │ │ ├── AlertWindow.java │ │ └── LauncherView.java │ └── res/ │ ├── layout/ │ │ ├── activity_main.xml │ │ └── window_launcher.xml │ └── values/ │ ├── array_string.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml /.idea/ /build/ .gradle /local.properties .DS_Store /captures .externalNativeBuild ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to AndPermission First off, thanks for taking the time to contribute. The following is a set of guidelines for contributing to AndPermission. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 1. All your actions in AndPermission should be in English, not in other languages. 2. Please keep AndPermission the existing code style, not according to your habits. 3. Just modify the code you are sure need to be optimized, not all the different code from your ideas. 4. Before launching a pull request, you should test your commit code adequately. 5. Please commit new code to the [dev](https://github.com/yanzhenjie/AndPermission/tree/dev) branch instead of the master branch. ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2019 Zhenjie Yan Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # AndPermission 1. Request for runtime permissions. 2. Share private files. 3. Request to install unknown source apk. `android.permission.REQUEST_INSTALL_PACKAGES` 4. Request to draw at the top of other apps. `android.permission.SYSTEM_ALERT_WINDOW` 5. Request to show notifications. 6. Request to access notifications. `android.permission.BIND_NOTIFICATION_LISTENER_SERVICE` 7. Request to modify system setting. `android.permission.WRITE_SETTINGS` ```java AndPermission.with(this) .runtime() .permission(Permission.Group.STORAGE) .onGranted(permissions -> { // Storage permission are allowed. }) .onDenied(permissions -> { // Storage permission are not allowed. }) .start(); ``` For documentation and additional information see [the website](https://yanzhenjie.com/AndPermission). ## Download It only supports androidx, add dependencies in your gradle: ```groovy implementation 'com.yanzhenjie:permission:2.0.3' ``` AndPermission requires at minimum Android 4.0(Api level 14) . ## Contributing Before submitting pull requests, contributors must abide by the [agreement](CONTRIBUTING.md) . ## License ```text Copyright 2019 Zhenjie Yan Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` ================================================ FILE: build.gradle ================================================ apply from: "config.gradle" buildscript { repositories { google() jcenter {url 'https://maven.aliyun.com/repository/jcenter'} jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.5.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' } } allprojects { repositories { google() jcenter {url 'https://maven.aliyun.com/repository/jcenter'} jcenter() } } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: config.gradle ================================================ ext { plugins = [application: 'com.android.application', library : 'com.android.library', maven : 'com.github.dcendents.android-maven', bintray : 'com.jfrog.bintray'] android = [applicationId : "com.yanzhenjie.permission.sample", compileSdkVersion: 29, buildToolsVersion: "29.0.2", minSdkVersion : 14, targetSdkVersion : 29, versionCode : 108, versionName : "2.0.3"] bintray = [version : "2.0.3", siteUrl : 'https://github.com/yanzhenjie/AndPermission', gitUrl : 'https://github.com/yanzhenjie/AndPermission.git', group : "com.yanzhenjie", packaging : 'aar', name : 'Permission', description : 'Permission manager for Android', licenseName : 'The Apache Software License, Version 2.0', licenseUrl : 'http://www.apache.org/licenses/LICENSE-2.0.txt', developerId : 'yanzhenjie', developerName : 'yanzhenjie', developerEmail: 'im.yanzhenjie@gmail.com', binrayLibrary : "permission", bintrayRepo : "maven", bintrayUser : 'yolanda', bintrayLicense: "Apache-2.0"] dependencies = [ fragment: 'androidx.appcompat:appcompat:1.1.0' ] } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip ================================================ FILE: gradle.properties ================================================ android.useAndroidX=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: permission/.gitignore ================================================ /build ================================================ FILE: permission/build.gradle ================================================ apply plugin: rootProject.ext.plugins.library android { compileSdkVersion rootProject.ext.android.compileSdkVersion buildToolsVersion rootProject.ext.android.buildToolsVersion defaultConfig { minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion } resourcePrefix 'permission' } dependencies { api rootProject.ext.dependencies.fragment } ================================================ FILE: permission/src/main/AndroidManifest.xml ================================================ ================================================ FILE: permission/src/main/aidl/com/yanzhenjie/permission/bridge/IBridge.aidl ================================================ package com.yanzhenjie.permission.bridge; interface IBridge { /** * Request for permissions. */ void requestAppDetails(in String suffix); /** * Request for permissions. */ void requestPermission(in String suffix, in String[] permissions); /** * Request for package install. */ void requestInstall(in String suffix); /** * Request for overlay. */ void requestOverlay(in String suffix); /** * Request for alert window. */ void requestAlertWindow(in String suffix); /** * Request for notify. */ void requestNotify(in String suffix); /** * Request for notification listener. */ void requestNotificationListener(in String suffix); /** * Request for write system setting. */ void requestWriteSetting(in String suffix); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/Action.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission; /** * Created by Zhenjie Yan on 2018/1/1. */ public interface Action { /** * An action. * * @param data the data. */ void onAction(T data); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/AndPermission.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission; import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; import android.net.Uri; import android.os.Build; import android.text.TextUtils; import androidx.fragment.app.Fragment; import com.yanzhenjie.permission.checker.DoubleChecker; import com.yanzhenjie.permission.checker.PermissionChecker; import com.yanzhenjie.permission.option.Option; import com.yanzhenjie.permission.source.ActivitySource; import com.yanzhenjie.permission.source.ContextSource; import com.yanzhenjie.permission.source.FragmentSource; import com.yanzhenjie.permission.source.Source; import com.yanzhenjie.permission.source.XFragmentSource; import java.io.File; import java.util.List; /** * Created by Zhenjie Yan on 2016/9/9. */ public class AndPermission { private static final String ACTION_BRIDGE_SUFFIX = ".andpermission.bridge"; public static String bridgeAction(Context context, String suffix) { return context.getPackageName() + ACTION_BRIDGE_SUFFIX + (TextUtils.isEmpty(suffix) ? "" : "." + suffix); } /** * With context. * * @param context {@link Context}. * @return {@link Option}. */ public static Option with(Context context) { return new Boot(getContextSource(context)); } /** * With {@link Fragment}. * * @param fragment {@link Fragment}. * @return {@link Option}. */ public static Option with(Fragment fragment) { return new Boot(new XFragmentSource(fragment)); } /** * With {@link android.app.Fragment}. * * @param fragment {@link android.app.Fragment}. * @return {@link Option}. */ public static Option with(android.app.Fragment fragment) { return new Boot(new FragmentSource(fragment)); } /** * With activity. * * @param activity {@link Activity}. * @return {@link Option}. */ public static Option with(Activity activity) { return new Boot(new ActivitySource(activity)); } /** * Some privileges permanently disabled, may need to set up in the execute. * * @param context {@link Context}. * @param deniedPermissions one or more permissions. * @return true, other wise is false. */ public static boolean hasAlwaysDeniedPermission(Context context, List deniedPermissions) { return hasAlwaysDeniedPermission(getContextSource(context), deniedPermissions); } /** * Some privileges permanently disabled, may need to set up in the execute. * * @param fragment {@link Fragment}. * @param deniedPermissions one or more permissions. * @return true, other wise is false. */ public static boolean hasAlwaysDeniedPermission(Fragment fragment, List deniedPermissions) { return hasAlwaysDeniedPermission(new XFragmentSource(fragment), deniedPermissions); } /** * Some privileges permanently disabled, may need to set up in the execute. * * @param fragment {@link android.app.Fragment}. * @param deniedPermissions one or more permissions. * @return true, other wise is false. */ public static boolean hasAlwaysDeniedPermission(android.app.Fragment fragment, List deniedPermissions) { return hasAlwaysDeniedPermission(new FragmentSource(fragment), deniedPermissions); } /** * Some privileges permanently disabled, may need to set up in the execute. * * @param activity {@link Activity}. * @param deniedPermissions one or more permissions. * @return true, other wise is false. */ public static boolean hasAlwaysDeniedPermission(Activity activity, List deniedPermissions) { return hasAlwaysDeniedPermission(new ActivitySource(activity), deniedPermissions); } /** * Has always been denied permission. */ private static boolean hasAlwaysDeniedPermission(Source source, List deniedPermissions) { for (String permission : deniedPermissions) { if (!source.isShowRationalePermission(permission)) { return true; } } return false; } /** * Some privileges permanently disabled, may need to set up in the execute. * * @param context {@link Context}. * @param deniedPermissions one or more permissions. * @return true, other wise is false. */ public static boolean hasAlwaysDeniedPermission(Context context, String... deniedPermissions) { return hasAlwaysDeniedPermission(getContextSource(context), deniedPermissions); } /** * Some privileges permanently disabled, may need to set up in the execute. * * @param fragment {@link Fragment}. * @param deniedPermissions one or more permissions. * @return true, other wise is false. */ public static boolean hasAlwaysDeniedPermission(Fragment fragment, String... deniedPermissions) { return hasAlwaysDeniedPermission(new XFragmentSource(fragment), deniedPermissions); } /** * Some privileges permanently disabled, may need to set up in the execute. * * @param fragment {@link android.app.Fragment}. * @param deniedPermissions one or more permissions. * @return true, other wise is false. */ public static boolean hasAlwaysDeniedPermission(android.app.Fragment fragment, String... deniedPermissions) { return hasAlwaysDeniedPermission(new FragmentSource(fragment), deniedPermissions); } /** * Some privileges permanently disabled, may need to set up in the execute. * * @param activity {@link Activity}. * @param deniedPermissions one or more permissions. * @return true, other wise is false. */ public static boolean hasAlwaysDeniedPermission(Activity activity, String... deniedPermissions) { return hasAlwaysDeniedPermission(new ActivitySource(activity), deniedPermissions); } /** * Has always been denied permission. */ private static boolean hasAlwaysDeniedPermission(Source source, String... deniedPermissions) { for (String permission : deniedPermissions) { if (!source.isShowRationalePermission(permission)) { return true; } } return false; } /** * Classic permission checker. */ private static final PermissionChecker PERMISSION_CHECKER = new DoubleChecker(); /** * Judgment already has the target permission. * * @param context {@link Context}. * @param permissions one or more permissions. * @return true, other wise is false. */ public static boolean hasPermissions(Context context, String... permissions) { return PERMISSION_CHECKER.hasPermission(context, permissions); } /** * Judgment already has the target permission. * * @param fragment {@link Fragment}. * @param permissions one or more permissions. * @return true, other wise is false. */ public static boolean hasPermissions(Fragment fragment, String... permissions) { return hasPermissions(fragment.getActivity(), permissions); } /** * Judgment already has the target permission. * * @param fragment {@link android.app.Fragment}. * @param permissions one or more permissions. * @return true, other wise is false. */ public static boolean hasPermissions(android.app.Fragment fragment, String... permissions) { return hasPermissions(fragment.getActivity(), permissions); } /** * Judgment already has the target permission. * * @param activity {@link Activity}. * @param permissions one or more permissions. * @return true, other wise is false. */ public static boolean hasPermissions(Activity activity, String... permissions) { return PERMISSION_CHECKER.hasPermission(activity, permissions); } /** * Judgment already has the target permission. * * @param context {@link Context}. * @param permissions one or more permission groups. * @return true, other wise is false. */ public static boolean hasPermissions(Context context, String[]... permissions) { for (String[] permission : permissions) { boolean hasPermission = PERMISSION_CHECKER.hasPermission(context, permission); if (!hasPermission) return false; } return true; } /** * Judgment already has the target permission. * * @param fragment {@link Fragment}. * @param permissions one or more permission groups. * @return true, other wise is false. */ public static boolean hasPermissions(Fragment fragment, String[]... permissions) { return hasPermissions(fragment.getActivity(), permissions); } /** * Judgment already has the target permission. * * @param fragment {@link android.app.Fragment}. * @param permissions one or more permission groups. * @return true, other wise is false. */ public static boolean hasPermissions(android.app.Fragment fragment, String[]... permissions) { return hasPermissions(fragment.getActivity(), permissions); } /** * Judgment already has the target permission. * * @param activity {@link Activity}. * @param permissions one or more permission groups. * @return true, other wise is false. */ public static boolean hasPermissions(Activity activity, String[]... permissions) { for (String[] permission : permissions) { boolean hasPermission = PERMISSION_CHECKER.hasPermission(activity, permission); if (!hasPermission) return false; } return true; } /** * Get compatible Android 7.0 and lower versions of Uri. * * @param context {@link Context}. * @param file apk file. * @return uri. */ public static Uri getFileUri(Context context, File file) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return FileProvider.getUriForFile(context, context.getPackageName() + ".file.path.share", file); } return Uri.fromFile(file); } /** * Get compatible Android 7.0 and lower versions of Uri. * * @param fragment {@link Fragment}. * @param file apk file. * @return uri. */ public static Uri getFileUri(Fragment fragment, File file) { return getFileUri(fragment.getContext(), file); } /** * Get compatible Android 7.0 and lower versions of Uri. * * @param fragment {@link android.app.Fragment}. * @param file apk file. * @return uri. */ public static Uri getFileUri(android.app.Fragment fragment, File file) { return getFileUri(fragment.getActivity(), file); } private static Source getContextSource(Context context) { if (context instanceof Activity) { return new ActivitySource((Activity) context); } else if (context instanceof ContextWrapper) { return getContextSource(((ContextWrapper) context).getBaseContext()); } return new ContextSource(context); } private AndPermission() { } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/Boot.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission; import android.os.Build; import com.yanzhenjie.permission.install.InstallRequest; import com.yanzhenjie.permission.install.NRequestFactory; import com.yanzhenjie.permission.install.ORequestFactory; import com.yanzhenjie.permission.notify.Notify; import com.yanzhenjie.permission.notify.option.NotifyOption; import com.yanzhenjie.permission.option.Option; import com.yanzhenjie.permission.overlay.LRequestFactory; import com.yanzhenjie.permission.overlay.MRequestFactory; import com.yanzhenjie.permission.overlay.OverlayRequest; import com.yanzhenjie.permission.runtime.Runtime; import com.yanzhenjie.permission.runtime.option.RuntimeOption; import com.yanzhenjie.permission.setting.Setting; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/4/28. */ public class Boot implements Option { private static final InstallRequestFactory INSTALL_REQUEST_FACTORY; private static final OverlayRequestFactory OVERLAY_REQUEST_FACTORY; static { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { INSTALL_REQUEST_FACTORY = new ORequestFactory(); } else { INSTALL_REQUEST_FACTORY = new NRequestFactory(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { OVERLAY_REQUEST_FACTORY = new MRequestFactory(); } else { OVERLAY_REQUEST_FACTORY = new LRequestFactory(); } } public interface InstallRequestFactory { /** * Create apk installer request. */ InstallRequest create(Source source); } public interface OverlayRequestFactory { /** * Create overlay request. */ OverlayRequest create(Source source); } private Source mSource; public Boot(Source source) { this.mSource = source; } @Override public RuntimeOption runtime() { return new Runtime(mSource); } @Override public InstallRequest install() { return INSTALL_REQUEST_FACTORY.create(mSource); } @Override public OverlayRequest overlay() { return OVERLAY_REQUEST_FACTORY.create(mSource); } @Override public NotifyOption notification() { return new Notify(mSource); } @Override public Setting setting() { return new Setting(mSource); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/FileProvider.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.res.XmlResourceParser; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.OpenableColumns; import android.text.TextUtils; import android.webkit.MimeTypeMap; import org.xmlpull.v1.XmlPullParserException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashMap; import java.util.Map; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; /** *

Copied from the support library v27.1.1.

* Created by Zhenjie Yan on 2018/4/28. */ public class FileProvider extends ContentProvider { private static final String[] COLUMNS = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}; private static final String META_DATA_FILE_PROVIDER_PATHS = "android.support.FILE_PROVIDER_PATHS"; private static final String TAG_ROOT_PATH = "root-path"; private static final String TAG_FILES_PATH = "files-path"; private static final String TAG_CACHE_PATH = "cache-path"; private static final String TAG_EXTERNAL = "external-path"; private static final String TAG_EXTERNAL_FILES = "external-files-path"; private static final String TAG_EXTERNAL_CACHE = "external-cache-path"; private static final String TAG_EXTERNAL_MEDIA = "external-media-path"; private static final String ATTR_NAME = "name"; private static final String ATTR_PATH = "path"; private static final File DEVICE_ROOT = new File("/"); private static final HashMap sCache = new HashMap<>(); private PathStrategy mStrategy; @Override public boolean onCreate() { return true; } @Override public void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); if (info.exported) { throw new SecurityException("Provider must not be exported"); } if (!info.grantUriPermissions) { throw new SecurityException("Provider must grant uri permissions"); } mStrategy = getPathStrategy(context, info.authority); } public static Uri getUriForFile(Context context, String authority, File file) { final PathStrategy strategy = getPathStrategy(context, authority); return strategy.getUriForFile(file); } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { final File file = mStrategy.getFileForUri(uri); if (projection == null) { projection = COLUMNS; } String[] cols = new String[projection.length]; Object[] values = new Object[projection.length]; int i = 0; for (String col : projection) { if (OpenableColumns.DISPLAY_NAME.equals(col)) { cols[i] = OpenableColumns.DISPLAY_NAME; values[i++] = file.getName(); } else if (OpenableColumns.SIZE.equals(col)) { cols[i] = OpenableColumns.SIZE; values[i++] = file.length(); } } cols = copyOf(cols, i); values = copyOf(values, i); final MatrixCursor cursor = new MatrixCursor(cols, 1); cursor.addRow(values); return cursor; } @Override public String getType(Uri uri) { final File file = mStrategy.getFileForUri(uri); final int lastDot = file.getName().lastIndexOf('.'); if (lastDot >= 0) { final String extension = file.getName().substring(lastDot + 1); final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); if (mime != null) { return mime; } } return "application/octet-stream"; } @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException("No external inserts"); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("No external updates"); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { final File file = mStrategy.getFileForUri(uri); return file.delete() ? 1 : 0; } @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { final File file = mStrategy.getFileForUri(uri); final int fileMode = modeToMode(mode); return ParcelFileDescriptor.open(file, fileMode); } private static PathStrategy getPathStrategy(Context context, String authority) { PathStrategy strategy; synchronized (sCache) { strategy = sCache.get(authority); if (strategy == null) { try { strategy = parsePathStrategy(context, authority); } catch (IOException e) { throw new IllegalArgumentException( "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e); } catch (XmlPullParserException e) { throw new IllegalArgumentException( "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e); } sCache.put(authority, strategy); } } return strategy; } private static PathStrategy parsePathStrategy(Context context, String authority) throws IOException, XmlPullParserException { final SimplePathStrategy strategy = new SimplePathStrategy(authority); final ProviderInfo info = context.getPackageManager() .resolveContentProvider(authority, PackageManager.GET_META_DATA); final XmlResourceParser in = info.loadXmlMetaData(context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS); if (in == null) { throw new IllegalArgumentException("Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data"); } int type; while ((type = in.next()) != END_DOCUMENT) { if (type == START_TAG) { final String tag = in.getName(); final String name = in.getAttributeValue(null, ATTR_NAME); String path = in.getAttributeValue(null, ATTR_PATH); File target = null; if (TAG_ROOT_PATH.equals(tag)) { target = DEVICE_ROOT; } else if (TAG_FILES_PATH.equals(tag)) { target = context.getFilesDir(); } else if (TAG_CACHE_PATH.equals(tag)) { target = context.getCacheDir(); } else if (TAG_EXTERNAL.equals(tag)) { target = Environment.getExternalStorageDirectory(); } else if (TAG_EXTERNAL_FILES.equals(tag)) { File[] externalFilesDirs = getExternalFilesDirs(context, null); if (externalFilesDirs.length > 0) { target = externalFilesDirs[0]; } } else if (TAG_EXTERNAL_CACHE.equals(tag)) { File[] externalCacheDirs = getExternalCacheDirs(context); if (externalCacheDirs.length > 0) { target = externalCacheDirs[0]; } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && TAG_EXTERNAL_MEDIA.equals(tag)) { File[] externalMediaDirs = context.getExternalMediaDirs(); if (externalMediaDirs.length > 0) { target = externalMediaDirs[0]; } } if (target != null) { strategy.addRoot(name, buildPath(target, path)); } } } return strategy; } interface PathStrategy { Uri getUriForFile(File file); File getFileForUri(Uri uri); } static class SimplePathStrategy implements PathStrategy { private final String mAuthority; private final HashMap mRoots = new HashMap(); SimplePathStrategy(String authority) { mAuthority = authority; } void addRoot(String name, File root) { if (TextUtils.isEmpty(name)) { throw new IllegalArgumentException("Name must not be empty"); } try { root = root.getCanonicalFile(); } catch (IOException e) { throw new IllegalArgumentException("Failed to resolve canonical path for " + root, e); } mRoots.put(name, root); } @Override public Uri getUriForFile(File file) { String path; try { path = file.getCanonicalPath(); } catch (IOException e) { throw new IllegalArgumentException("Failed to resolve canonical path for " + file); } Map.Entry mostSpecific = null; for (Map.Entry root : mRoots.entrySet()) { final String rootPath = root.getValue().getPath(); boolean invalidMost = mostSpecific == null || rootPath.length() > mostSpecific.getValue().getPath().length(); if (path.startsWith(rootPath) && invalidMost) { mostSpecific = root; } } if (mostSpecific == null) { throw new IllegalArgumentException("Failed to find configured root that contains " + path); } final String rootPath = mostSpecific.getValue().getPath(); if (rootPath.endsWith("/")) { path = path.substring(rootPath.length()); } else { path = path.substring(rootPath.length() + 1); } path = Uri.encode(mostSpecific.getKey()) + '/' + Uri.encode(path, "/"); return new Uri.Builder().scheme("content").authority(mAuthority).encodedPath(path).build(); } @Override public File getFileForUri(Uri uri) { String path = uri.getEncodedPath(); final int splitIndex = path.indexOf('/', 1); final String tag = Uri.decode(path.substring(1, splitIndex)); path = Uri.decode(path.substring(splitIndex + 1)); final File root = mRoots.get(tag); if (root == null) { throw new IllegalArgumentException("Unable to find configured root for " + uri); } File file = new File(root, path); try { file = file.getCanonicalFile(); } catch (IOException e) { throw new IllegalArgumentException("Failed to resolve canonical path for " + file); } if (!file.getPath().startsWith(root.getPath())) { throw new SecurityException("Resolved path jumped beyond configured root"); } return file; } } private static int modeToMode(String mode) { int modeBits; if ("r".equals(mode)) { modeBits = ParcelFileDescriptor.MODE_READ_ONLY; } else if ("w".equals(mode) || "wt".equals(mode)) { modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE; } else if ("wa".equals(mode)) { modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_APPEND; } else if ("rw".equals(mode)) { modeBits = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE; } else if ("rwt".equals(mode)) { modeBits = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE; } else { throw new IllegalArgumentException("Invalid mode: " + mode); } return modeBits; } private static File buildPath(File base, String... segments) { File cur = base; for (String segment : segments) { if (segment != null) { cur = new File(cur, segment); } } return cur; } private static String[] copyOf(String[] original, int newLength) { final String[] result = new String[newLength]; System.arraycopy(original, 0, result, 0, newLength); return result; } private static Object[] copyOf(Object[] original, int newLength) { final Object[] result = new Object[newLength]; System.arraycopy(original, 0, result, 0, newLength); return result; } private static File[] getExternalFilesDirs(Context context, String type) { if (Build.VERSION.SDK_INT >= 19) { return context.getExternalFilesDirs(type); } else { return new File[] {context.getExternalFilesDir(type)}; } } public static File[] getExternalCacheDirs(Context context) { if (Build.VERSION.SDK_INT >= 19) { return context.getExternalCacheDirs(); } else { return new File[] {context.getExternalCacheDir()}; } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/Rationale.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission; import android.content.Context; /** * Created by Zhenjie Yan on 2016/9/10. */ public interface Rationale { /** * Show rationale to user. * * @param context context. * @param data the data. * @param executor executor. */ void showRationale(Context context, T data, RequestExecutor executor); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/RequestExecutor.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission; /** *

Request executor.

* Created by Zhenjie Yan on 2016/9/10. */ public interface RequestExecutor { /** * Go request permission. */ void execute(); /** * Cancel the operation. */ void cancel(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeActivity.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.bridge; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.view.KeyEvent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.yanzhenjie.permission.overlay.setting.LSettingPage; import com.yanzhenjie.permission.overlay.setting.MSettingPage; import com.yanzhenjie.permission.source.ActivitySource; import com.yanzhenjie.permission.source.Source; /** *

* Request permission. *

* Created by Zhenjie Yan on 2017/4/27. */ public final class BridgeActivity extends Activity { private static final String KEY_TYPE = "KEY_TYPE"; private static final String KEY_PERMISSIONS = "KEY_PERMISSIONS"; private static final String KEY_ACTION_SUFFIX = "KEY_ACTION_SUFFIX"; /** * Request for permissions. */ static void requestAppDetails(Source source, String suffix) { Intent intent = new Intent(source.getContext(), BridgeActivity.class); intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_APP_DETAILS); intent.putExtra(KEY_ACTION_SUFFIX, suffix); source.startActivity(intent); } /** * Request for permissions. */ static void requestPermission(Source source, String suffix, String[] permissions) { Intent intent = new Intent(source.getContext(), BridgeActivity.class); intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_PERMISSION); intent.putExtra(KEY_PERMISSIONS, permissions); intent.putExtra(KEY_ACTION_SUFFIX, suffix); source.startActivity(intent); } /** * Request for package install. */ static void requestInstall(Source source, String suffix) { Intent intent = new Intent(source.getContext(), BridgeActivity.class); intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_INSTALL); intent.putExtra(KEY_ACTION_SUFFIX, suffix); source.startActivity(intent); } /** * Request for overlay. */ static void requestOverlay(Source source, String suffix) { Intent intent = new Intent(source.getContext(), BridgeActivity.class); intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_OVERLAY); intent.putExtra(KEY_ACTION_SUFFIX, suffix); source.startActivity(intent); } /** * Request for alert window. */ static void requestAlertWindow(Source source, String suffix) { Intent intent = new Intent(source.getContext(), BridgeActivity.class); intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_ALERT_WINDOW); intent.putExtra(KEY_ACTION_SUFFIX, suffix); source.startActivity(intent); } /** * Request for notify. */ static void requestNotify(Source source, String suffix) { Intent intent = new Intent(source.getContext(), BridgeActivity.class); intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_NOTIFY); intent.putExtra(KEY_ACTION_SUFFIX, suffix); source.startActivity(intent); } /** * Request for notification listener. */ static void requestNotificationListener(Source source, String suffix) { Intent intent = new Intent(source.getContext(), BridgeActivity.class); intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_NOTIFY_LISTENER); intent.putExtra(KEY_ACTION_SUFFIX, suffix); source.startActivity(intent); } /** * Request for write system setting. */ static void requestWriteSetting(Source source, String suffix) { Intent intent = new Intent(source.getContext(), BridgeActivity.class); intent.putExtra(KEY_TYPE, BridgeRequest.TYPE_WRITE_SETTING); intent.putExtra(KEY_ACTION_SUFFIX, suffix); source.startActivity(intent); } private String mActionSuffix; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) return; Intent intent = getIntent(); int operation = intent.getIntExtra(KEY_TYPE, -1); mActionSuffix = intent.getStringExtra(KEY_ACTION_SUFFIX); switch (operation) { case BridgeRequest.TYPE_APP_DETAILS: { Intent appDetailsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); appDetailsIntent.setData(Uri.fromParts("package", getPackageName(), null)); startActivityForResult(appDetailsIntent, BridgeRequest.TYPE_APP_DETAILS); break; } case BridgeRequest.TYPE_PERMISSION: { String[] permissions = intent.getStringArrayExtra(KEY_PERMISSIONS); requestPermissions(permissions, BridgeRequest.TYPE_PERMISSION); break; } case BridgeRequest.TYPE_INSTALL: { Intent manageIntent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); manageIntent.setData(Uri.fromParts("package", getPackageName(), null)); startActivityForResult(manageIntent, BridgeRequest.TYPE_INSTALL); break; } case BridgeRequest.TYPE_OVERLAY: { MSettingPage settingPage = new MSettingPage(new ActivitySource(this)); settingPage.start(BridgeRequest.TYPE_OVERLAY); break; } case BridgeRequest.TYPE_ALERT_WINDOW: { LSettingPage settingPage = new LSettingPage(new ActivitySource(this)); settingPage.start(BridgeRequest.TYPE_ALERT_WINDOW); break; } case BridgeRequest.TYPE_NOTIFY: { Intent settingIntent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); settingIntent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); settingIntent.setData(Uri.fromParts("package", getPackageName(), null)); startActivityForResult(settingIntent, BridgeRequest.TYPE_NOTIFY); break; } case BridgeRequest.TYPE_NOTIFY_LISTENER: { Intent settingIntent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS); settingIntent.setData(Uri.fromParts("package", getPackageName(), null)); startActivityForResult(settingIntent, BridgeRequest.TYPE_NOTIFY_LISTENER); break; } case BridgeRequest.TYPE_WRITE_SETTING: { Intent settingIntent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); settingIntent.setData(Uri.fromParts("package", getPackageName(), null)); startActivityForResult(settingIntent, BridgeRequest.TYPE_WRITE_SETTING); break; } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { Messenger.send(this, mActionSuffix); finish(); } @Override public void startActivityForResult(Intent intent, int requestCode) { super.startActivityForResult(intent, requestCode); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); Messenger.send(this, mActionSuffix); finish(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { return true; } return super.onKeyDown(keyCode, event); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeRequest.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.bridge; import com.yanzhenjie.permission.source.Source; import java.util.List; /** * Created by Zhenjie Yan on 2/13/19. */ public final class BridgeRequest { public static final int TYPE_APP_DETAILS = 1; public static final int TYPE_PERMISSION = 2; public static final int TYPE_INSTALL = 3; public static final int TYPE_OVERLAY = 4; public static final int TYPE_ALERT_WINDOW = 5; public static final int TYPE_NOTIFY = 6; public static final int TYPE_NOTIFY_LISTENER = 7; public static final int TYPE_WRITE_SETTING = 8; private final Source mSource; private int mType; private Callback mCallback; private List mPermissions; public BridgeRequest(Source source) { this.mSource = source; } public Source getSource() { return mSource; } public int getType() { return mType; } public void setType(int type) { mType = type; } public Callback getCallback() { return mCallback; } public void setCallback(Callback callback) { mCallback = callback; } public List getPermissions() { return mPermissions; } public void setPermissions(List permissions) { mPermissions = permissions; } public interface Callback { void onCallback(); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/BridgeService.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.bridge; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import androidx.annotation.Nullable; import com.yanzhenjie.permission.source.ContextSource; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2019-08-30. */ public class BridgeService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return mStub.asBinder(); } private IBridge.Stub mStub = new IBridge.Stub() { private Source mSource = new ContextSource(BridgeService.this); @Override public void requestAppDetails(String suffix) throws RemoteException { BridgeActivity.requestAppDetails(mSource, suffix); } @Override public void requestPermission(String suffix, String[] permissions) throws RemoteException { BridgeActivity.requestPermission(mSource, suffix, permissions); } @Override public void requestInstall(String suffix) throws RemoteException { BridgeActivity.requestInstall(mSource, suffix); } @Override public void requestOverlay(String suffix) throws RemoteException { BridgeActivity.requestOverlay(mSource, suffix); } @Override public void requestAlertWindow(String suffix) throws RemoteException { BridgeActivity.requestAlertWindow(mSource, suffix); } @Override public void requestNotify(String suffix) throws RemoteException { BridgeActivity.requestNotify(mSource, suffix); } @Override public void requestNotificationListener(String suffix) throws RemoteException { BridgeActivity.requestNotificationListener(mSource, suffix); } @Override public void requestWriteSetting(String suffix) throws RemoteException { BridgeActivity.requestWriteSetting(mSource, suffix); } }; } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/Messenger.java ================================================ /* * Copyright © 2018 Zhenjie Yan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.bridge; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import com.yanzhenjie.permission.AndPermission; /** * Created by Zhenjie Yan on 2018/6/9. */ class Messenger extends BroadcastReceiver { public static void send(Context context, String suffix) { Intent broadcast = new Intent(AndPermission.bridgeAction(context, suffix)); context.sendBroadcast(broadcast); } private final Context mContext; private final Callback mCallback; public Messenger(Context context, Callback callback) { this.mContext = context; this.mCallback = callback; } public void register(String suffix) { IntentFilter filter = new IntentFilter(AndPermission.bridgeAction(mContext, suffix)); mContext.registerReceiver(this, filter); } public void unRegister() { mContext.unregisterReceiver(this); } @Override public void onReceive(Context context, Intent intent) { mCallback.onCallback(); } public interface Callback { void onCallback(); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/RequestExecutor.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.bridge; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import com.yanzhenjie.permission.AndPermission; import java.util.List; /** * Created by Zhenjie Yan on 2/13/19. */ final class RequestExecutor extends Thread implements Messenger.Callback { private BridgeRequest mRequest; private Messenger mMessenger; public RequestExecutor(BridgeRequest request) { this.mRequest = request; } @Override public void run() { Context context = mRequest.getSource().getContext(); mMessenger = new Messenger(context, this); mMessenger.register(getName()); Intent intent = new Intent(); intent.setAction(AndPermission.bridgeAction(context, null)); intent.setPackage(context.getPackageName()); context.bindService(intent, mConnection, Service.BIND_AUTO_CREATE); } private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { IBridge iBridge = IBridge.Stub.asInterface(iBinder); try { executeCurrent(iBridge); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { } }; private void executeCurrent(IBridge iBridge) throws RemoteException { switch (mRequest.getType()) { case BridgeRequest.TYPE_APP_DETAILS: { iBridge.requestAppDetails(getName()); break; } case BridgeRequest.TYPE_PERMISSION: { List permissions = mRequest.getPermissions(); String[] array = permissions.toArray(new String[0]); iBridge.requestPermission(getName(), array); break; } case BridgeRequest.TYPE_INSTALL: { iBridge.requestInstall(getName()); break; } case BridgeRequest.TYPE_OVERLAY: { iBridge.requestOverlay(getName()); break; } case BridgeRequest.TYPE_ALERT_WINDOW: { iBridge.requestAlertWindow(getName()); break; } case BridgeRequest.TYPE_NOTIFY: { iBridge.requestNotify(getName()); break; } case BridgeRequest.TYPE_NOTIFY_LISTENER: { iBridge.requestNotificationListener(getName()); break; } case BridgeRequest.TYPE_WRITE_SETTING: { iBridge.requestWriteSetting(getName()); break; } } } @Override public void onCallback() { synchronized (this) { mMessenger.unRegister(); mRequest.getCallback().onCallback(); mRequest.getSource().getContext().unbindService(mConnection); mMessenger = null; mRequest = null; } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/bridge/RequestManager.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.bridge; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Created by Zhenjie Yan on 2/13/19. */ public class RequestManager { private static RequestManager sManager; public static RequestManager get() { if (sManager == null) { synchronized (RequestManager.class) { if (sManager == null) { sManager = new RequestManager(); } } } return sManager; } private final Executor mExecutor; private RequestManager() { this.mExecutor = Executors.newCachedThreadPool(); } public void add(BridgeRequest request) { mExecutor.execute(new RequestExecutor(request)); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CalendarReadTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.provider.CalendarContract; /** * Created by Zhenjie Yan on 2018/1/25. */ class CalendarReadTest implements PermissionTest { private ContentResolver mResolver; CalendarReadTest(Context context) { mResolver = context.getContentResolver(); } @Override public boolean test() throws Throwable { String[] projection = new String[] {CalendarContract.Calendars._ID, CalendarContract.Calendars.NAME}; Cursor cursor = mResolver.query(CalendarContract.Calendars.CONTENT_URI, projection, null, null, null); if (cursor != null) { try { CursorTest.read(cursor); } finally { cursor.close(); } return true; } else { return false; } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CalendarWriteTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.graphics.Color; import android.net.Uri; import android.provider.CalendarContract; import com.yanzhenjie.permission.util.StringUtils; import java.util.TimeZone; /** * Created by Zhenjie Yan on 2018/1/15. */ class CalendarWriteTest implements PermissionTest { private static final String NAME = StringUtils.hexToText("5045524D495353494F4E"); private static final String ACCOUNT = StringUtils.hexToText("7065726D697373696F6E40676D61696C2E636F6D"); private ContentResolver mResolver; CalendarWriteTest(Context context) { this.mResolver = context.getContentResolver(); } @Override public boolean test() throws Throwable { try { TimeZone timeZone = TimeZone.getDefault(); ContentValues value = new ContentValues(); value.put(CalendarContract.Calendars.NAME, NAME); value.put(CalendarContract.Calendars.ACCOUNT_NAME, ACCOUNT); value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL); value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, NAME); value.put(CalendarContract.Calendars.VISIBLE, 1); value.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.BLUE); value.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER); value.put(CalendarContract.Calendars.SYNC_EVENTS, 1); value.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timeZone.getID()); value.put(CalendarContract.Calendars.OWNER_ACCOUNT, NAME); value.put(CalendarContract.Calendars.CAN_ORGANIZER_RESPOND, 0); Uri insertUri = CalendarContract.Calendars.CONTENT_URI.buildUpon() .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true") .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, NAME) .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL) .build(); Uri resourceUri = mResolver.insert(insertUri, value); return ContentUris.parseId(resourceUri) > 0; } finally { Uri deleteUri = CalendarContract.Calendars.CONTENT_URI.buildUpon().build(); mResolver.delete(deleteUri, CalendarContract.Calendars.ACCOUNT_NAME + "=?", new String[]{ACCOUNT}); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CallLogReadTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.provider.CallLog; /** * Created by Zhenjie Yan on 2018/1/25. */ class CallLogReadTest implements PermissionTest { private ContentResolver mResolver; CallLogReadTest(Context context) { mResolver = context.getContentResolver(); } @Override public boolean test() throws Throwable { String[] projection = new String[] {CallLog.Calls._ID, CallLog.Calls.NUMBER, CallLog.Calls.TYPE}; Cursor cursor = mResolver.query(CallLog.Calls.CONTENT_URI, projection, null, null, null); if (cursor != null) { try { CursorTest.read(cursor); } finally { cursor.close(); } return true; } else { return false; } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CallLogWriteTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.net.Uri; import android.provider.CallLog; /** * Created by Zhenjie Yan on 2018/1/14. */ class CallLogWriteTest implements PermissionTest { private ContentResolver mResolver; CallLogWriteTest(Context context) { this.mResolver = context.getContentResolver(); } @Override public boolean test() throws Throwable { try { ContentValues content = new ContentValues(); content.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE); content.put(CallLog.Calls.NUMBER, "1"); content.put(CallLog.Calls.DATE, 20080808); content.put(CallLog.Calls.NEW, "0"); Uri resourceUri = mResolver.insert(CallLog.Calls.CONTENT_URI, content); return ContentUris.parseId(resourceUri) > 0; } finally { mResolver.delete(CallLog.Calls.CONTENT_URI, CallLog.Calls.NUMBER + "=?", new String[] {"1"}); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/CameraTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.Camera; /** * Created by Zhenjie Yan on 2018/1/15. */ class CameraTest implements PermissionTest { private Context mContext; CameraTest(Context context) { this.mContext = context; } @Override public boolean test() throws Throwable { Camera camera = null; try { int cameraCount = Camera.getNumberOfCameras(); if (cameraCount <= 0) return true; camera = Camera.open(cameraCount - 1); Camera.Parameters parameters = camera.getParameters(); camera.setParameters(parameters); camera.setPreviewCallback(PREVIEW_CALLBACK); camera.startPreview(); return true; } catch (Throwable e) { PackageManager packageManager = mContext.getPackageManager(); return !packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA); } finally { if (camera != null) { camera.stopPreview(); camera.setPreviewCallback(null); camera.release(); } } } private static final Camera.PreviewCallback PREVIEW_CALLBACK = new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { } }; } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/ContactsReadTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.provider.ContactsContract; /** * Created by Zhenjie Yan on 2018/1/25. */ class ContactsReadTest implements PermissionTest { private ContentResolver mResolver; ContactsReadTest(Context context) { mResolver = context.getContentResolver(); } @Override public boolean test() throws Throwable { String[] projection = new String[] {ContactsContract.Data._ID, ContactsContract.Data.DATA1}; Cursor cursor = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null); if (cursor != null) { try { CursorTest.read(cursor); } finally { cursor.close(); } return true; } else { return false; } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/ContactsWriteTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; import androidx.annotation.Nullable; /** * Created by Zhenjie Yan on 2018/1/14. */ class ContactsWriteTest implements PermissionTest { private static final String DISPLAY_NAME = "PERMISSION"; private ContentResolver mResolver; ContactsWriteTest(ContentResolver resolver) { this.mResolver = resolver; } @Override public boolean test() throws Throwable { long[] idArray = insert(); long rawContactId = idArray[0]; long dataId = idArray[1]; if (rawContactId > 0 && dataId > 0) { return delete(rawContactId, dataId); } return false; } private long[] insert() { ContentValues values = new ContentValues(); Uri rawContractUri = mResolver.insert(ContactsContract.RawContacts.CONTENT_URI, values); long rawContactId = ContentUris.parseId(rawContractUri); values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); values.put(ContactsContract.Data.DATA1, DISPLAY_NAME); values.put(ContactsContract.Data.DATA2, DISPLAY_NAME); values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); Uri dataUri = mResolver.insert(ContactsContract.Data.CONTENT_URI, values); long dataId = ContentUris.parseId(dataUri); return new long[]{rawContactId, dataId}; } private boolean delete(long rawContactId, long dataId) { int dataCount = mResolver.delete(ContactsContract.Data.CONTENT_URI, ContactsContract.Data._ID + "=?", new String[]{Long.toString(dataId)}); int rawContactCount = mResolver.delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.RawContacts._ID + "=?", new String[]{Long.toString(rawContactId)}); return rawContactCount > 0 && dataCount > 0; } private boolean update(long rawContactId) { ContentValues values = new ContentValues(); values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); values.put(ContactsContract.Data.DATA1, DISPLAY_NAME); values.put(ContactsContract.Data.DATA2, DISPLAY_NAME); values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); Uri dataUri = mResolver.insert(ContactsContract.Data.CONTENT_URI, values); return ContentUris.parseId(dataUri) > 0; } @Nullable private long[] query() { Cursor cursor = mResolver.query(ContactsContract.Data.CONTENT_URI, new String[]{ContactsContract.Data.RAW_CONTACT_ID, ContactsContract.Data._ID}, ContactsContract.Data.MIMETYPE + "=? and " + ContactsContract.Data.DATA1 + "=?", new String[]{ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, DISPLAY_NAME}, null); if (cursor != null) { if (cursor.moveToFirst()) { long rawContactId = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID)); long dataId = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data._ID)); return new long[]{rawContactId, dataId}; } } return null; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/DoubleChecker.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import java.util.List; /** * Created by Zhenjie Yan on 2018/1/27. */ public final class DoubleChecker implements PermissionChecker { private static final PermissionChecker STANDARD_CHECKER = new StandardChecker(); private static final PermissionChecker STRICT_CHECKER = new StrictChecker(); @Override public boolean hasPermission(Context context, String... permissions) { return STRICT_CHECKER.hasPermission(context, permissions) && STANDARD_CHECKER.hasPermission(context, permissions); } @Override public boolean hasPermission(Context context, List permissions) { return STRICT_CHECKER.hasPermission(context, permissions) && STANDARD_CHECKER.hasPermission(context, permissions); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/LocationCoarseTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.content.pm.PackageManager; import android.location.LocationManager; import java.util.List; /** * Created by Zhenjie Yan on 2018/1/14. */ class LocationCoarseTest implements PermissionTest { private Context mContext; LocationCoarseTest(Context context) { this.mContext = context; } @Override public boolean test() throws Throwable { LocationManager locationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE); List providers = locationManager.getProviders(true); boolean networkProvider = providers.contains(LocationManager.NETWORK_PROVIDER); if (networkProvider) { return true; } PackageManager packageManager = mContext.getPackageManager(); boolean networkHardware = packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_NETWORK); if (!networkHardware) return true; return !locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/LocationFineTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.content.pm.PackageManager; import android.location.LocationManager; import java.util.List; /** * Created by Zhenjie Yan on 2018/1/14. */ class LocationFineTest implements PermissionTest { private Context mContext; LocationFineTest(Context context) { this.mContext = context; } @Override public boolean test() throws Throwable { LocationManager locationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE); List providers = locationManager.getProviders(true); boolean gpsProvider = providers.contains(LocationManager.GPS_PROVIDER); boolean passiveProvider = providers.contains(LocationManager.PASSIVE_PROVIDER); if (gpsProvider || passiveProvider) { return true; } PackageManager packageManager = mContext.getPackageManager(); boolean gpsHardware = packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS); if (!gpsHardware) return true; return !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PermissionChecker.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import java.util.List; /** * Created by Zhenjie Yan on 2018/1/7. */ public interface PermissionChecker { /** * Check if the calling context has a set of permissions. * * @param context {@link Context}. * @param permissions one or more permissions. * * @return true, other wise is false. */ boolean hasPermission(Context context, String... permissions); /** * Check if the calling context has a set of permissions. * * @param context {@link Context}. * @param permissions one or more permissions. * * @return true, other wise is false. */ boolean hasPermission(Context context, List permissions); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PermissionTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.database.Cursor; /** * Created by Zhenjie Yan on 2018/1/14. */ interface PermissionTest { boolean test() throws Throwable; class CursorTest { public static void read(Cursor cursor) { int count = cursor.getCount(); if (count > 0) { cursor.moveToFirst(); int type = cursor.getType(0); switch (type) { case Cursor.FIELD_TYPE_BLOB: case Cursor.FIELD_TYPE_NULL: { break; } case Cursor.FIELD_TYPE_INTEGER: case Cursor.FIELD_TYPE_FLOAT: case Cursor.FIELD_TYPE_STRING: default: { cursor.getString(0); break; } } } } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/PhoneStateReadTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import android.telephony.TelephonyManager; /** * Created by Zhenjie Yan on 2018/1/25. */ class PhoneStateReadTest implements PermissionTest { private Context mContext; PhoneStateReadTest(Context context) { this.mContext = context; } @Override public boolean test() throws Throwable { PackageManager packageManager = mContext.getPackageManager(); if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) return true; TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) telephonyManager.getDeviceId(); else telephonyManager.getDeviceSoftwareVersion(); return true; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/RecordAudioTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * Created by Zhenjie Yan on 2018/1/14. */ class RecordAudioTest implements PermissionTest { private static final int[] RATES = new int[]{8000, 11025, 22050, 44100}; private Context mContext; RecordAudioTest(Context context) { this.mContext = context; } @Override public boolean test() throws Throwable { AudioRecord audioRecord = null; File file = null; FileOutputStream fos = null; try { int[] params = findAudioParameters(); if (params == null) return !existMicrophone(mContext); audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, params[0], params[1], params[2], params[3]); int state = audioRecord.getState(); if (state != AudioRecord.STATE_INITIALIZED) return !existMicrophone(mContext); int recordState = audioRecord.getRecordingState(); if (recordState != AudioRecord.RECORDSTATE_STOPPED) return true; audioRecord.startRecording(); if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) return true; File cacheDir = new File(mContext.getCacheDir(), "_andpermission_audio_record_test_"); cacheDir.mkdirs(); file = new File(cacheDir, Long.toString(System.currentTimeMillis())); if (file.exists()) file.createNewFile(); fos = new FileOutputStream(file); byte[] buffer = new byte[params[3]]; int len = audioRecord.read(buffer, 0, params[3]); fos.write(buffer, 0, len); fos.flush(); } catch (Throwable e) { return !existMicrophone(mContext); } finally { if (fos != null) { try { fos.close(); } catch (IOException ignored) { } } if (file != null && file.exists()) { file.delete(); } if (audioRecord != null) { audioRecord.release(); } } return true; } public static boolean existMicrophone(Context context) { PackageManager packageManager = context.getPackageManager(); return packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE); } public static int[] findAudioParameters() { for (int rate : RATES) { for (int channel : new int[]{AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO}) { for (int format : new int[]{AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT}) { int buffer = AudioRecord.getMinBufferSize(rate, channel, format); if (buffer != AudioRecord.ERROR_BAD_VALUE) { return new int[]{rate, channel, format, buffer}; } } } } return null; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SensorActivityTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; /** * Created by Zhenjie Yan on 2018/1/25. */ class SensorActivityTest implements PermissionTest { private Context mContext; SensorActivityTest(Context context) { this.mContext = context; } @Override public boolean test() throws Throwable { SensorManager sensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); try { Sensor heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR); sensorManager.registerListener(SENSOR_EVENT_LISTENER, heartRateSensor, 3); sensorManager.unregisterListener(SENSOR_EVENT_LISTENER, heartRateSensor); } catch (Throwable e) { PackageManager packageManager = mContext.getPackageManager(); return !packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR); } return true; } private static final SensorEventListener SENSOR_EVENT_LISTENER = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SensorHeartTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; /** * Created by Zhenjie Yan on 2018/1/25. */ class SensorHeartTest implements PermissionTest { private Context mContext; SensorHeartTest(Context context) { this.mContext = context; } @Override public boolean test() throws Throwable { SensorManager sensorManager = (SensorManager)mContext.getSystemService(Context.SENSOR_SERVICE); try { Sensor heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE); sensorManager.registerListener(SENSOR_EVENT_LISTENER, heartRateSensor, 3); sensorManager.unregisterListener(SENSOR_EVENT_LISTENER, heartRateSensor); } catch (Throwable e) { PackageManager packageManager = mContext.getPackageManager(); return !packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_HEART_RATE); } return true; } private static final SensorEventListener SENSOR_EVENT_LISTENER = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SipTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.net.sip.SipManager; import android.net.sip.SipProfile; import com.yanzhenjie.permission.util.StringUtils; /** * Created by Zhenjie Yan on 2018/1/25. */ class SipTest implements PermissionTest { private static final String USER = StringUtils.hexToText("5065726D697373696F6E"); private static final String IP = StringUtils.hexToText("3132372E302E302E31"); private static final String PASSWORD = StringUtils.textToHex("70617373776F7264"); private Context mContext; SipTest(Context context) { this.mContext = context; } @Override public boolean test() throws Throwable { if (!SipManager.isApiSupported(mContext)) { return true; } SipManager manager = SipManager.newInstance(mContext); if (manager == null) { return true; } SipProfile.Builder builder = new SipProfile.Builder(USER, IP); builder.setPassword(PASSWORD); SipProfile profile = builder.build(); manager.open(profile); manager.close(profile.getUriString()); return true; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/SmsReadTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.provider.Telephony; /** * Created by Zhenjie Yan on 2018/1/25. */ class SmsReadTest implements PermissionTest { private ContentResolver mResolver; SmsReadTest(Context context) { mResolver = context.getContentResolver(); } @Override public boolean test() throws Throwable { String[] projection = new String[] {Telephony.Sms._ID, Telephony.Sms.ADDRESS, Telephony.Sms.PERSON, Telephony.Sms.BODY}; Cursor cursor = mResolver.query(Telephony.Sms.CONTENT_URI, projection, null, null, null); if (cursor != null) { try { CursorTest.read(cursor); } finally { cursor.close(); } return true; } else { return false; } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StandardChecker.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import android.text.TextUtils; import java.util.Arrays; import java.util.List; /** * Created by Zhenjie Yan on 2018/1/7. */ public final class StandardChecker implements PermissionChecker { private static final int MODE_ASK = 4; private static final int MODE_COMPAT = 5; public StandardChecker() { } @Override public boolean hasPermission(Context context, String... permissions) { return hasPermission(context, Arrays.asList(permissions)); } @Override public boolean hasPermission(Context context, List permissions) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true; AppOpsManager opsManager = null; for (String permission : permissions) { int result = context.checkPermission(permission, android.os.Process.myPid(), android.os.Process.myUid()); if (result == PackageManager.PERMISSION_DENIED) { return false; } String op = AppOpsManager.permissionToOp(permission); if (TextUtils.isEmpty(op)) { continue; } if (opsManager == null) opsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); result = opsManager.checkOpNoThrow(op, android.os.Process.myUid(), context.getPackageName()); if (result != AppOpsManager.MODE_ALLOWED && result != MODE_ASK && result != MODE_COMPAT) { return false; } } return true; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StorageReadTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.os.Build; import android.os.Environment; import android.text.TextUtils; import java.io.File; /** * Created by Zhenjie Yan on 2018/1/16. */ class StorageReadTest implements PermissionTest { StorageReadTest() { } @Override public boolean test() throws Throwable { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !Environment.isExternalStorageLegacy()) return true; if (!TextUtils.equals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState())) return true; File directory = Environment.getExternalStorageDirectory(); if (!directory.exists()) return true; long modified = directory.lastModified(); String[] pathList = directory.list(); return modified > 0 && pathList != null; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StorageWriteTest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.Context; import android.os.Build; import android.os.Environment; import android.text.TextUtils; import java.io.File; /** * Created by Zhenjie Yan on 2018/1/16. */ class StorageWriteTest implements PermissionTest { private Context mContext; StorageWriteTest(Context c) { mContext = c; } @Override public boolean test() throws Throwable { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !Environment.isExternalStorageLegacy()) return true; if (!TextUtils.equals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState())) return true; File directory = mContext.getExternalFilesDir(null); if (!directory.exists()) return true; File parent = new File(directory, "Android"); if (parent.exists() && parent.isFile()) { if (!parent.delete()) return false; } if (!parent.exists()) { if (!parent.mkdirs()) return false; } File file = new File(parent, "ANDROID.PERMISSION.TEST"); if (file.exists()) { return file.delete(); } else { return file.createNewFile(); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/checker/StrictChecker.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.checker; import android.content.ContentResolver; import android.content.Context; import android.os.Build; import com.yanzhenjie.permission.runtime.Permission; import java.util.List; /** * Created by Zhenjie Yan on 2018/1/7. */ public final class StrictChecker implements PermissionChecker { public StrictChecker() { } @Override public boolean hasPermission(Context context, String... permissions) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return true; for (String permission : permissions) { if (!hasPermission(context, permission)) { return false; } } return true; } @Override public boolean hasPermission(Context context, List permissions) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return true; for (String permission : permissions) { if (!hasPermission(context, permission)) { return false; } } return true; } private boolean hasPermission(Context context, String permission) { try { switch (permission) { case Permission.READ_CALENDAR: return checkReadCalendar(context); case Permission.WRITE_CALENDAR: return checkWriteCalendar(context); case Permission.CAMERA: return checkCamera(context); case Permission.READ_CONTACTS: return checkReadContacts(context); case Permission.WRITE_CONTACTS: return checkWriteContacts(context); case Permission.GET_ACCOUNTS: return true; case Permission.ACCESS_COARSE_LOCATION: return checkCoarseLocation(context); case Permission.ACCESS_FINE_LOCATION: return checkFineLocation(context); case Permission.RECORD_AUDIO: return checkRecordAudio(context); case Permission.READ_PHONE_STATE: return checkReadPhoneState(context); case Permission.CALL_PHONE: return true; case Permission.READ_CALL_LOG: return checkReadCallLog(context); case Permission.WRITE_CALL_LOG: return checkWriteCallLog(context); case Permission.ADD_VOICEMAIL: return true; case Permission.USE_SIP: return checkSip(context); case Permission.PROCESS_OUTGOING_CALLS: return true; case Permission.BODY_SENSORS: return checkSensorHeart(context); case Permission.ACTIVITY_RECOGNITION: return checkSensorActivity(context); case Permission.SEND_SMS: case Permission.RECEIVE_MMS: return true; case Permission.READ_SMS: return checkReadSms(context); case Permission.RECEIVE_WAP_PUSH: case Permission.RECEIVE_SMS: return true; case Permission.READ_EXTERNAL_STORAGE: return checkReadStorage(); case Permission.WRITE_EXTERNAL_STORAGE: return checkWriteStorage(context); } } catch (Throwable e) { return false; } return true; } private static boolean checkReadCalendar(Context context) throws Throwable { PermissionTest test = new CalendarReadTest(context); return test.test(); } private static boolean checkWriteCalendar(Context context) throws Throwable { PermissionTest test = new CalendarWriteTest(context); return test.test(); } private static boolean checkCamera(Context context) throws Throwable { PermissionTest test = new CameraTest(context); return test.test(); } private static boolean checkReadContacts(Context context) throws Throwable { PermissionTest test = new ContactsReadTest(context); return test.test(); } private static boolean checkWriteContacts(Context context) throws Throwable { ContentResolver resolver = context.getContentResolver(); PermissionTest test = new ContactsWriteTest(resolver); return test.test(); } private static boolean checkCoarseLocation(Context context) throws Throwable { PermissionTest test = new LocationCoarseTest(context); return test.test(); } private static boolean checkFineLocation(Context context) throws Throwable { PermissionTest test = new LocationFineTest(context); return test.test(); } private static boolean checkRecordAudio(Context context) throws Throwable { PermissionTest test = new RecordAudioTest(context); return test.test(); } private static boolean checkReadPhoneState(Context context) throws Throwable { PermissionTest test = new PhoneStateReadTest(context); return test.test(); } private static boolean checkReadCallLog(Context context) throws Throwable { PermissionTest test = new CallLogReadTest(context); return test.test(); } private static boolean checkWriteCallLog(Context context) throws Throwable { PermissionTest test = new CallLogWriteTest(context); return test.test(); } private static boolean checkSip(Context context) throws Throwable { PermissionTest test = new SipTest(context); return test.test(); } private static boolean checkSensorHeart(Context context) throws Throwable { PermissionTest test = new SensorHeartTest(context); return test.test(); } private static boolean checkSensorActivity(Context context) throws Throwable { PermissionTest test = new SensorActivityTest(context); return test.test(); } private static boolean checkReadSms(Context context) throws Throwable { PermissionTest test = new SmsReadTest(context); return test.test(); } private static boolean checkReadStorage() throws Throwable { PermissionTest test = new StorageReadTest(); return test.test(); } private static boolean checkWriteStorage(Context context) throws Throwable { PermissionTest test = new StorageWriteTest(context); return test.test(); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/install/BaseRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.install; import android.content.Context; import android.content.Intent; import android.net.Uri; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.AndPermission; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.source.Source; import java.io.File; /** * Created by Zhenjie Yan on 2018/6/1. */ abstract class BaseRequest implements InstallRequest { private Source mSource; private File mFile; private Rationale mRationale = new Rationale() { @Override public void showRationale(Context context, File data, RequestExecutor executor) { executor.execute(); } }; private Action mGranted; private Action mDenied; BaseRequest(Source source) { this.mSource = source; } @Override public final InstallRequest file(File file) { this.mFile = file; return this; } @Override public final InstallRequest rationale(Rationale rationale) { this.mRationale = rationale; return this; } @Override public final InstallRequest onGranted(Action granted) { this.mGranted = granted; return this; } @Override public final InstallRequest onDenied(Action denied) { this.mDenied = denied; return this; } /** * Why permissions are required. */ final void showRationale(RequestExecutor executor) { mRationale.showRationale(mSource.getContext(), null, executor); } /** * Start the installation. */ final void install() { if (mFile != null) { Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri uri = AndPermission.getFileUri(mSource.getContext(), mFile); intent.setDataAndType(uri, "application/vnd.android.package-archive"); mSource.startActivity(intent); } } /** * Callback acceptance status. */ final void callbackSucceed() { if (mGranted != null) { mGranted.onAction(mFile); } } /** * Callback rejected state. */ final void callbackFailed() { if (mDenied != null) { mDenied.onAction(mFile); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/install/InstallRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.install; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; import java.io.File; /** * Created by Zhenjie Yan on 2018/4/28. */ public interface InstallRequest { /** * The apk file. * * @param file apk file. */ InstallRequest file(File file); /** * Set request rationale. */ InstallRequest rationale(Rationale rationale); /** * Action to be taken when all permissions are granted. */ InstallRequest onGranted(Action granted); /** * Action to be taken when all permissions are denied. */ InstallRequest onDenied(Action denied); /** * Start install. */ void start(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/install/NRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.install; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/4/28. */ class NRequest extends BaseRequest { NRequest(Source source) { super(source); } @Override public void start() { callbackSucceed(); install(); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/install/NRequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.install; import com.yanzhenjie.permission.Boot; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/4/28. */ public class NRequestFactory implements Boot.InstallRequestFactory { @Override public InstallRequest create(Source source) { return new NRequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/install/ORequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.install; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.bridge.BridgeRequest; import com.yanzhenjie.permission.bridge.RequestManager; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/4/28. */ class ORequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback { private Source mSource; ORequest(Source source) { super(source); this.mSource = source; } @Override public void start() { if (mSource.canRequestPackageInstalls()) { callbackSucceed(); install(); } else { showRationale(this); } } @Override public void execute() { BridgeRequest request = new BridgeRequest(mSource); request.setType(BridgeRequest.TYPE_INSTALL); request.setCallback(this); RequestManager.get().add(request); } @Override public void cancel() { callbackFailed(); } @Override public void onCallback() { if (mSource.canRequestPackageInstalls()) { callbackSucceed(); install(); } else { callbackFailed(); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/install/ORequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.install; import com.yanzhenjie.permission.Boot; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/4/28. */ public class ORequestFactory implements Boot.InstallRequestFactory { @Override public InstallRequest create(Source source) { return new ORequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/BaseRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify; import android.content.Context; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/6/1. */ abstract class BaseRequest implements PermissionRequest { private Source mSource; private Rationale mRationale = new Rationale() { @Override public void showRationale(Context context, Void data, RequestExecutor executor) { executor.execute(); } }; private Action mGranted; private Action mDenied; BaseRequest(Source source) { this.mSource = source; } @Override public final PermissionRequest rationale(Rationale rationale) { this.mRationale = rationale; return this; } @Override public final PermissionRequest onGranted(Action granted) { this.mGranted = granted; return this; } @Override public final PermissionRequest onDenied(Action denied) { this.mDenied = denied; return this; } /** * Why permissions are required. */ final void showRationale(RequestExecutor executor) { mRationale.showRationale(mSource.getContext(), null, executor); } /** * Callback acceptance status. */ final void callbackSucceed() { if (mGranted != null) { mGranted.onAction(null); } } /** * Callback rejected state. */ final void callbackFailed() { if (mDenied != null) { mDenied.onAction(null); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/NRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.bridge.BridgeRequest; import com.yanzhenjie.permission.bridge.RequestManager; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ class NRequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback { private Source mSource; NRequest(Source source) { super(source); this.mSource = source; } @Override public void start() { if (mSource.canNotify()) { callbackSucceed(); } else { showRationale(this); } } @Override public void execute() { BridgeRequest request = new BridgeRequest(mSource); request.setType(BridgeRequest.TYPE_APP_DETAILS); request.setCallback(this); RequestManager.get().add(request); } @Override public void cancel() { callbackFailed(); } @Override public void onCallback() { if (mSource.canNotify()) { callbackSucceed(); } else { callbackFailed(); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/NRequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ public class NRequestFactory implements Notify.PermissionRequestFactory { @Override public PermissionRequest create(Source source) { return new NRequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/Notify.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify; import android.os.Build; import com.yanzhenjie.permission.notify.listener.J1RequestFactory; import com.yanzhenjie.permission.notify.listener.J2RequestFactory; import com.yanzhenjie.permission.notify.listener.ListenerRequest; import com.yanzhenjie.permission.notify.option.NotifyOption; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2/22/19. */ public class Notify implements NotifyOption { private static final PermissionRequestFactory PERMISSION_REQUEST_FACTORY; private static final ListenerRequestFactory LISTENER_REQUEST_FACTORY; static { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { PERMISSION_REQUEST_FACTORY = new ORequestFactory(); } else { PERMISSION_REQUEST_FACTORY = new NRequestFactory(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { LISTENER_REQUEST_FACTORY = new J2RequestFactory(); } else { LISTENER_REQUEST_FACTORY = new J1RequestFactory(); } } public interface PermissionRequestFactory { /** * Create notify request. */ PermissionRequest create(Source source); } public interface ListenerRequestFactory { /** * Create notification listener request. */ ListenerRequest create(Source source); } private Source mSource; public Notify(Source source) { this.mSource = source; } public PermissionRequest permission() { return PERMISSION_REQUEST_FACTORY.create(mSource); } public ListenerRequest listener() { return LISTENER_REQUEST_FACTORY.create(mSource); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/ORequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.bridge.BridgeRequest; import com.yanzhenjie.permission.bridge.RequestManager; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ class ORequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback { private Source mSource; ORequest(Source source) { super(source); this.mSource = source; } @Override public void start() { if (mSource.canNotify()) { callbackSucceed(); } else { showRationale(this); } } @Override public void execute() { BridgeRequest request = new BridgeRequest(mSource); request.setType(BridgeRequest.TYPE_NOTIFY); request.setCallback(this); RequestManager.get().add(request); } @Override public void cancel() { callbackFailed(); } @Override public void onCallback() { if (mSource.canNotify()) { callbackSucceed(); } else { callbackFailed(); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/ORequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ public class ORequestFactory implements Notify.PermissionRequestFactory { @Override public PermissionRequest create(Source source) { return new ORequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/PermissionRequest.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; /** * Created by Zhenjie Yan on 2/14/19. */ public interface PermissionRequest { /** * Set request rationale. */ PermissionRequest rationale(Rationale rationale); /** * Action to be taken when all permissions are granted. */ PermissionRequest onGranted(Action granted); /** * Action to be taken when all permissions are denied. */ PermissionRequest onDenied(Action denied); /** * Start install. */ void start(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/BaseRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify.listener; import android.content.Context; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/6/1. */ abstract class BaseRequest implements ListenerRequest { private Source mSource; private Rationale mRationale = new Rationale() { @Override public void showRationale(Context context, Void data, RequestExecutor executor) { executor.execute(); } }; private Action mGranted; private Action mDenied; BaseRequest(Source source) { this.mSource = source; } @Override public final ListenerRequest rationale(Rationale rationale) { this.mRationale = rationale; return this; } @Override public final ListenerRequest onGranted(Action granted) { this.mGranted = granted; return this; } @Override public final ListenerRequest onDenied(Action denied) { this.mDenied = denied; return this; } /** * Why permissions are required. */ final void showRationale(RequestExecutor executor) { mRationale.showRationale(mSource.getContext(), null, executor); } /** * Callback acceptance status. */ final void callbackSucceed() { if (mGranted != null) { mGranted.onAction(null); } } /** * Callback rejected state. */ final void callbackFailed() { if (mDenied != null) { mDenied.onAction(null); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J1Request.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify.listener; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ class J1Request extends BaseRequest implements RequestExecutor { J1Request(Source source) { super(source); } @Override public void start() { callbackSucceed(); } @Override public void execute() { // Nothing. } @Override public void cancel() { // Nothing. } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J1RequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify.listener; import com.yanzhenjie.permission.notify.Notify; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ public class J1RequestFactory implements Notify.ListenerRequestFactory { @Override public ListenerRequest create(Source source) { return new J1Request(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J2Request.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify.listener; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.bridge.BridgeRequest; import com.yanzhenjie.permission.bridge.RequestManager; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ class J2Request extends BaseRequest implements RequestExecutor, BridgeRequest.Callback { private Source mSource; J2Request(Source source) { super(source); this.mSource = source; } @Override public void start() { if (mSource.canListenerNotification()) { callbackSucceed(); } else { showRationale(this); } } @Override public void execute() { BridgeRequest request = new BridgeRequest(mSource); request.setType(BridgeRequest.TYPE_NOTIFY_LISTENER); request.setCallback(this); RequestManager.get().add(request); } @Override public void cancel() { callbackFailed(); } @Override public void onCallback() { if (mSource.canListenerNotification()) { callbackSucceed(); } else { callbackFailed(); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/J2RequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify.listener; import com.yanzhenjie.permission.notify.Notify; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ public class J2RequestFactory implements Notify.ListenerRequestFactory { @Override public ListenerRequest create(Source source) { return new J2Request(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/listener/ListenerRequest.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify.listener; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; /** * Created by Zhenjie Yan on 2/14/19. */ public interface ListenerRequest { /** * Set request rationale. */ ListenerRequest rationale(Rationale rationale); /** * Action to be taken when all permissions are granted. */ ListenerRequest onGranted(Action granted); /** * Action to be taken when all permissions are denied. */ ListenerRequest onDenied(Action denied); /** * Start install. */ void start(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/notify/option/NotifyOption.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.notify.option; import com.yanzhenjie.permission.notify.PermissionRequest; import com.yanzhenjie.permission.notify.listener.ListenerRequest; /** * Created by Zhenjie Yan on 2/14/19. */ public interface NotifyOption { /** * Handle permissions. */ PermissionRequest permission(); /** * Handle notify listener. */ ListenerRequest listener(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/option/Option.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.option; import com.yanzhenjie.permission.install.InstallRequest; import com.yanzhenjie.permission.notify.option.NotifyOption; import com.yanzhenjie.permission.overlay.OverlayRequest; import com.yanzhenjie.permission.runtime.option.RuntimeOption; import com.yanzhenjie.permission.setting.Setting; /** * Created by Zhenjie Yan on 2/22/19. */ public interface Option { /** * Handle runtime permissions. */ RuntimeOption runtime(); /** * Handle request package install permission. */ InstallRequest install(); /** * Handle overlay permission. */ OverlayRequest overlay(); /** * Handle notification permission. */ NotifyOption notification(); /** * Handle system setting. */ Setting setting(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/BaseRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.overlay; import android.app.Dialog; import android.content.Context; import android.os.Build; import android.view.Window; import android.view.WindowManager; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.R; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/6/1. */ abstract class BaseRequest implements OverlayRequest { private Source mSource; private Rationale mRationale = new Rationale() { @Override public void showRationale(Context context, Void data, RequestExecutor executor) { executor.execute(); } }; private Action mGranted; private Action mDenied; BaseRequest(Source source) { this.mSource = source; } @Override public final OverlayRequest rationale(Rationale rationale) { this.mRationale = rationale; return this; } @Override public final OverlayRequest onGranted(Action granted) { this.mGranted = granted; return this; } @Override public final OverlayRequest onDenied(Action denied) { this.mDenied = denied; return this; } /** * Why permissions are required. */ final void showRationale(RequestExecutor executor) { mRationale.showRationale(mSource.getContext(), null, executor); } /** * Callback acceptance status. */ final void callbackSucceed() { if (mGranted != null) { mGranted.onAction(null); } } /** * Callback rejected state. */ final void callbackFailed() { if (mDenied != null) { mDenied.onAction(null); } } static boolean tryDisplayDialog(Context context) { Dialog dialog = new Dialog(context, R.style.Permission_Theme_Dialog_Transparent); Window window = dialog.getWindow(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else { window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); } try { dialog.show(); } catch (Exception e) { return false; } finally { if (dialog.isShowing()) dialog.dismiss(); } return true; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/LRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.overlay; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.bridge.BridgeRequest; import com.yanzhenjie.permission.bridge.RequestManager; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ class LRequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback { private Source mSource; LRequest(Source source) { super(source); this.mSource = source; } @Override public void start() { if (tryDisplayDialog(mSource.getContext())) { callbackSucceed(); } else { showRationale(this); } } @Override public void execute() { BridgeRequest request = new BridgeRequest(mSource); request.setType(BridgeRequest.TYPE_ALERT_WINDOW); request.setCallback(this); RequestManager.get().add(request); } @Override public void cancel() { callbackFailed(); } @Override public void onCallback() { if (tryDisplayDialog(mSource.getContext())) { callbackSucceed(); } else { callbackFailed(); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/LRequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.overlay; import com.yanzhenjie.permission.Boot; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ public class LRequestFactory implements Boot.OverlayRequestFactory { @Override public OverlayRequest create(Source source) { return new LRequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/MRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.overlay; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.bridge.BridgeRequest; import com.yanzhenjie.permission.bridge.RequestManager; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ class MRequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback { private Source mSource; MRequest(Source source) { super(source); this.mSource = source; } @Override public void start() { if (mSource.canDrawOverlays()) { onCallback(); } else { showRationale(this); } } @Override public void execute() { BridgeRequest request = new BridgeRequest(mSource); request.setType(BridgeRequest.TYPE_OVERLAY); request.setCallback(this); RequestManager.get().add(request); } @Override public void cancel() { callbackFailed(); } @Override public void onCallback() { if (mSource.canDrawOverlays() && tryDisplayDialog(mSource.getContext())) { callbackSucceed(); } else { callbackFailed(); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/MRequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.overlay; import com.yanzhenjie.permission.Boot; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ public class MRequestFactory implements Boot.OverlayRequestFactory { @Override public OverlayRequest create(Source source) { return new MRequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/OverlayRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.overlay; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; /** * Created by Zhenjie Yan on 2018/5/29. */ public interface OverlayRequest { /** * Set request rationale. */ OverlayRequest rationale(Rationale rationale); /** * Action to be taken when all permissions are granted. */ OverlayRequest onGranted(Action granted); /** * Action to be taken when all permissions are denied. */ OverlayRequest onDenied(Action denied); /** * Start request. */ void start(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/setting/LSettingPage.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.overlay.setting; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.provider.Settings; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/29. */ public class LSettingPage { private static final String MARK = Build.MANUFACTURER.toLowerCase(); private Source mSource; public LSettingPage(Source source) { this.mSource = source; } public void start(int requestCode) { Intent intent; if (MARK.contains("huawei")) { intent = huaweiApi(mSource.getContext()); } else if (MARK.contains("xiaomi")) { intent = xiaomiApi(mSource.getContext()); } else if (MARK.contains("oppo")) { intent = oppoApi(mSource.getContext()); } else if (MARK.contains("vivo")) { intent = vivoApi(mSource.getContext()); } else if (MARK.contains("meizu")) { intent = meizuApi(mSource.getContext()); } else { intent = defaultApi(mSource.getContext()); } try { mSource.startActivityForResult(intent, requestCode); } catch (Exception e) { intent = defaultApi(mSource.getContext()); mSource.startActivityForResult(intent, requestCode); } } private static Intent defaultApi(Context context) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.fromParts("package", context.getPackageName(), null)); return intent; } private Intent huaweiApi(Context context) { Intent intent = new Intent(); intent.putExtra("package", context.getPackageName()); intent.putExtra("packageName", context.getPackageName()); intent.setData(Uri.fromParts("package", context.getPackageName(), null)); intent.setClassName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity"); if (hasActivity(context, intent)) return intent; intent.setClassName("com.huawei.systemmanager", "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity"); if (hasActivity(context, intent)) return intent; intent.setClassName("com.huawei.systemmanager", "com.huawei.notificationmanager.ui.NotificationManagmentActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private Intent xiaomiApi(Context context) { Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); intent.putExtra("extra_pkgname", context.getPackageName()); if (hasActivity(context, intent)) return intent; intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private Intent oppoApi(Context context) { Intent intent = new Intent(); intent.putExtra("packageName", context.getPackageName()); intent.setClassName("com.color.safecenter", "com.color.safecenter.permission.floatwindow.FloatWindowListActivity"); if (hasActivity(context, intent)) return intent; intent.setClassName("com.coloros.safecenter", "com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity"); if (hasActivity(context, intent)) return intent; intent.setClassName("com.oppo.safe", "com.oppo.safe.permission.PermissionAppListActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private Intent vivoApi(Context context) { Intent intent = new Intent(); intent.setClassName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.FloatWindowManager"); intent.putExtra("packagename", context.getPackageName()); if (hasActivity(context, intent)) return intent; intent.setClassName("com.iqoo.secure", "com.iqoo.secure.safeguard.SoftPermissionDetailActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private Intent meizuApi(Context context) { Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); intent.putExtra("packageName", context.getPackageName()); intent.setComponent(new ComponentName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity")); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private static boolean hasActivity(Context context, Intent intent) { PackageManager packageManager = context.getPackageManager(); return packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/overlay/setting/MSettingPage.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.overlay.setting; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.provider.Settings; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/5/30. */ public class MSettingPage { private static final String MARK = Build.MANUFACTURER.toLowerCase(); private Source mSource; public MSettingPage(Source source) { this.mSource = source; } public void start(int requestCode) { Intent intent; if (MARK.contains("meizu")) { intent = meiZuApi(mSource.getContext()); } else { intent = defaultApi(mSource.getContext()); } try { mSource.startActivityForResult(intent, requestCode); } catch (Exception e) { intent = appDetailsApi(mSource.getContext()); mSource.startActivityForResult(intent, requestCode); } } private static Intent appDetailsApi(Context context) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.fromParts("package", context.getPackageName(), null)); return intent; } private static Intent defaultApi(Context context) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); intent.setData(Uri.fromParts("package", context.getPackageName(), null)); if (hasActivity(context, intent)) return intent; return appDetailsApi(context); } private static Intent meiZuApi(Context context) { Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); intent.putExtra("packageName", context.getPackageName()); intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private static boolean hasActivity(Context context, Intent intent) { PackageManager packageManager = context.getPackageManager(); return packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/BaseRequest.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import android.content.Context; import android.os.Build; import androidx.annotation.NonNull; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.checker.PermissionChecker; import com.yanzhenjie.permission.source.Source; import java.util.ArrayList; import java.util.HashSet; import java.util.List; /** * Created Zhenjie Yan on 2019-10-10. */ abstract class BaseRequest implements PermissionRequest { private Source mSource; private Rationale> mRationale = new Rationale>() { @Override public void showRationale(Context context, List data, RequestExecutor executor) { executor.execute(); } }; private Action> mGranted; private Action> mDenied; BaseRequest(Source source) { this.mSource = source; } @Override public PermissionRequest rationale(@NonNull Rationale> rationale) { this.mRationale = rationale; return this; } @Override public PermissionRequest onGranted(@NonNull Action> granted) { this.mGranted = granted; return this; } @Override public PermissionRequest onDenied(@NonNull Action> denied) { this.mDenied = denied; return this; } /** * Why permissions are required. */ final void showRationale(List rationaleList, RequestExecutor executor) { mRationale.showRationale(mSource.getContext(), rationaleList, executor); } /** * Callback acceptance status. */ final void callbackSucceed(List grantedList) { if (mGranted != null) { mGranted.onAction(grantedList); } } /** * Callback rejected state. */ final void callbackFailed(List deniedList) { if (mDenied != null) { mDenied.onAction(deniedList); } } /** * Filter the permissions you want to apply; remove unsupported and duplicate permissions. */ public static List filterPermissions(List permissions) { permissions = new ArrayList<>(new HashSet<>(permissions)); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { permissions.remove(Permission.READ_PHONE_NUMBERS); permissions.remove(Permission.ANSWER_PHONE_CALLS); } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { permissions.remove(Permission.ACTIVITY_RECOGNITION); permissions.remove(Permission.ACCESS_BACKGROUND_LOCATION); } return permissions; } /** * Get denied permissions. */ public static List getDeniedPermissions(PermissionChecker checker, Source source, List permissions) { List deniedList = new ArrayList<>(1); for (String permission : permissions) { if (!checker.hasPermission(source.getContext(), permission)) { deniedList.add(permission); } } return deniedList; } /** * Get permissions to show rationale. */ public static List getRationalePermissions(Source source, List deniedPermissions) { List rationaleList = new ArrayList<>(1); for (String permission : deniedPermissions) { if (source.isShowRationalePermission(permission)) { rationaleList.add(permission); } } return rationaleList; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/LRequest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import androidx.annotation.NonNull; import com.yanzhenjie.permission.checker.PermissionChecker; import com.yanzhenjie.permission.checker.StrictChecker; import com.yanzhenjie.permission.source.Source; import com.yanzhenjie.permission.task.TaskExecutor; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Created by Zhenjie Yan on 2018/1/25. */ class LRequest extends BaseRequest { private static final PermissionChecker STRICT_CHECKER = new StrictChecker(); private Source mSource; private List mPermissions; LRequest(Source source) { super(source); this.mSource = source; } @Override public PermissionRequest permission(@NonNull String... permissions) { mPermissions = new ArrayList<>(); mPermissions.addAll(Arrays.asList(permissions)); return this; } @Override public PermissionRequest permission(@NonNull String[]... groups) { mPermissions = new ArrayList<>(); for (String[] group : groups) { mPermissions.addAll(Arrays.asList(group)); } return this; } @Override public void start() { mPermissions = filterPermissions(mPermissions); new TaskExecutor>(mSource.getContext()) { @Override protected List doInBackground(Void... voids) { return getDeniedPermissions(STRICT_CHECKER, mSource, mPermissions); } @Override protected void onFinish(List deniedList) { if (deniedList.isEmpty()) { callbackSucceed(mPermissions); } else { callbackFailed(deniedList); } } }.execute(); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/LRequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/4/28. */ public class LRequestFactory implements Runtime.PermissionRequestFactory { @Override public PermissionRequest create(Source source) { return new LRequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/MRequest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import androidx.annotation.NonNull; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.bridge.BridgeRequest; import com.yanzhenjie.permission.bridge.RequestManager; import com.yanzhenjie.permission.checker.DoubleChecker; import com.yanzhenjie.permission.checker.PermissionChecker; import com.yanzhenjie.permission.checker.StandardChecker; import com.yanzhenjie.permission.source.Source; import com.yanzhenjie.permission.task.TaskExecutor; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Created by Zhenjie Yan on 2016/9/9. */ class MRequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback { private static final PermissionChecker STANDARD_CHECKER = new StandardChecker(); private static final PermissionChecker DOUBLE_CHECKER = new DoubleChecker(); private Source mSource; private List mPermissions; private List mDeniedPermissions; MRequest(Source source) { super(source); this.mSource = source; } @Override public PermissionRequest permission(@NonNull String... permissions) { mPermissions = new ArrayList<>(); mPermissions.addAll(Arrays.asList(permissions)); return this; } @Override public PermissionRequest permission(@NonNull String[]... groups) { mPermissions = new ArrayList<>(); for (String[] group : groups) { mPermissions.addAll(Arrays.asList(group)); } return this; } @Override public void start() { mPermissions = filterPermissions(mPermissions); mDeniedPermissions = getDeniedPermissions(STANDARD_CHECKER, mSource, mPermissions); if (mDeniedPermissions.size() > 0) { List rationaleList = getRationalePermissions(mSource, mDeniedPermissions); if (rationaleList.size() > 0) { showRationale(rationaleList, this); } else { execute(); } } else { onCallback(); } } @Override public void execute() { BridgeRequest request = new BridgeRequest(mSource); request.setType(BridgeRequest.TYPE_PERMISSION); request.setPermissions(mDeniedPermissions); request.setCallback(this); RequestManager.get().add(request); } @Override public void cancel() { onCallback(); } @Override public void onCallback() { new TaskExecutor>(mSource.getContext()) { @Override protected List doInBackground(Void... voids) { return getDeniedPermissions(DOUBLE_CHECKER, mSource, mPermissions); } @Override protected void onFinish(List deniedList) { if (deniedList.isEmpty()) { callbackSucceed(mPermissions); } else { callbackFailed(deniedList); } } }.execute(); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/MRequestFactory.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/4/28. */ public class MRequestFactory implements Runtime.PermissionRequestFactory { @Override public PermissionRequest create(Source source) { return new MRequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/Permission.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import android.content.Context; import android.os.Build; import com.yanzhenjie.permission.R; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** *

Permissions.

* Created by Zhenjie Yan on 2017/8/4. */ public class Permission { public static final String READ_CALENDAR = "android.permission.READ_CALENDAR"; public static final String WRITE_CALENDAR = "android.permission.WRITE_CALENDAR"; public static final String CAMERA = "android.permission.CAMERA"; public static final String READ_CONTACTS = "android.permission.READ_CONTACTS"; public static final String WRITE_CONTACTS = "android.permission.WRITE_CONTACTS"; public static final String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; public static final String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"; public static final String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"; public static final String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION"; public static final String RECORD_AUDIO = "android.permission.RECORD_AUDIO"; public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"; public static final String CALL_PHONE = "android.permission.CALL_PHONE"; public static final String USE_SIP = "android.permission.USE_SIP"; public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"; public static final String ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS"; public static final String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG"; public static final String WRITE_CALL_LOG = "android.permission.WRITE_CALL_LOG"; public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"; public static final String BODY_SENSORS = "android.permission.BODY_SENSORS"; public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION"; public static final String SEND_SMS = "android.permission.SEND_SMS"; public static final String RECEIVE_SMS = "android.permission.RECEIVE_SMS"; public static final String READ_SMS = "android.permission.READ_SMS"; public static final String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH"; public static final String RECEIVE_MMS = "android.permission.RECEIVE_MMS"; public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"; public static final String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"; public static final class Group { public static final String[] CALENDAR = new String[]{Permission.READ_CALENDAR, Permission.WRITE_CALENDAR}; public static final String[] CAMERA = new String[]{Permission.CAMERA}; public static final String[] CONTACTS = new String[]{Permission.READ_CONTACTS, Permission.WRITE_CONTACTS, Permission.GET_ACCOUNTS}; public static final String[] LOCATION = new String[]{Permission.ACCESS_FINE_LOCATION, Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_BACKGROUND_LOCATION}; public static final String[] MICROPHONE = new String[]{Permission.RECORD_AUDIO}; public static final String[] PHONE = new String[]{Permission.READ_PHONE_STATE, Permission.CALL_PHONE, Permission.USE_SIP, Permission.READ_PHONE_NUMBERS, Permission.ANSWER_PHONE_CALLS, Permission.ADD_VOICEMAIL}; public static final String[] CALL_LOG = new String[]{Permission.READ_CALL_LOG, Permission.WRITE_CALL_LOG, Permission.PROCESS_OUTGOING_CALLS}; public static final String[] SENSORS = new String[]{Permission.BODY_SENSORS}; public static final String[] ACTIVITY_RECOGNITION = new String[]{Permission.ACTIVITY_RECOGNITION}; public static final String[] SMS = new String[]{Permission.SEND_SMS, Permission.RECEIVE_SMS, Permission.READ_SMS, Permission.RECEIVE_WAP_PUSH, Permission.RECEIVE_MMS}; public static final String[] STORAGE = new String[]{Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE}; } /** * Turn permissions into text. */ public static List transformText(Context context, String... permissions) { return transformText(context, Arrays.asList(permissions)); } /** * Turn permissions into text. */ public static List transformText(Context context, String[]... groups) { List permissionList = new ArrayList<>(); for (String[] group : groups) { permissionList.addAll(Arrays.asList(group)); } return transformText(context, permissionList); } /** * Turn permissions into text. */ public static List transformText(Context context, List permissions) { List textList = new ArrayList<>(); for (String permission : permissions) { switch (permission) { case Permission.READ_CALENDAR: case Permission.WRITE_CALENDAR: { String message = context.getString(R.string.permission_name_calendar); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.CAMERA: { String message = context.getString(R.string.permission_name_camera); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.GET_ACCOUNTS: case Permission.READ_CONTACTS: case Permission.WRITE_CONTACTS: { String message = context.getString(R.string.permission_name_contacts); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.ACCESS_FINE_LOCATION: case Permission.ACCESS_COARSE_LOCATION: { String message = context.getString(R.string.permission_name_location); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.RECORD_AUDIO: { String message = context.getString(R.string.permission_name_microphone); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.READ_PHONE_STATE: case Permission.CALL_PHONE: case Permission.ADD_VOICEMAIL: case Permission.USE_SIP: case Permission.READ_PHONE_NUMBERS: case Permission.ANSWER_PHONE_CALLS: { String message = context.getString(R.string.permission_name_phone); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.READ_CALL_LOG: case Permission.WRITE_CALL_LOG: case Permission.PROCESS_OUTGOING_CALLS: { int messageId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? R.string.permission_name_call_log : R.string.permission_name_phone; String message = context.getString(messageId); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.BODY_SENSORS: { String message = context.getString(R.string.permission_name_sensors); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.ACTIVITY_RECOGNITION: { String message = context.getString(R.string.permission_name_activity_recognition); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.SEND_SMS: case Permission.RECEIVE_SMS: case Permission.READ_SMS: case Permission.RECEIVE_WAP_PUSH: case Permission.RECEIVE_MMS: { String message = context.getString(R.string.permission_name_sms); if (!textList.contains(message)) { textList.add(message); } break; } case Permission.READ_EXTERNAL_STORAGE: case Permission.WRITE_EXTERNAL_STORAGE: { String message = context.getString(R.string.permission_name_storage); if (!textList.contains(message)) { textList.add(message); } break; } } } return textList; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/PermissionDef.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import androidx.annotation.StringDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Created Zhenjie Yan on 2019-10-10. */ @StringDef({ Permission.READ_CALENDAR, Permission.WRITE_CALENDAR, Permission.CAMERA, Permission.READ_CONTACTS, Permission.WRITE_CONTACTS, Permission.GET_ACCOUNTS, Permission.ACCESS_FINE_LOCATION, Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_BACKGROUND_LOCATION, Permission.RECORD_AUDIO, Permission.READ_PHONE_STATE, Permission.CALL_PHONE, Permission.ADD_VOICEMAIL, Permission.USE_SIP, Permission.READ_PHONE_NUMBERS, Permission.ANSWER_PHONE_CALLS, Permission.READ_CALL_LOG, Permission.WRITE_CALL_LOG, Permission.PROCESS_OUTGOING_CALLS, Permission.BODY_SENSORS, Permission.ACTIVITY_RECOGNITION, Permission.SEND_SMS, Permission.RECEIVE_SMS, Permission.READ_SMS, Permission.RECEIVE_WAP_PUSH, Permission.RECEIVE_MMS, Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE }) @Retention(RetentionPolicy.SOURCE) public @interface PermissionDef { } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/PermissionRequest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import androidx.annotation.NonNull; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; import java.util.List; /** *

Permission request.

* Created by Zhenjie Yan on 2016/9/9. */ public interface PermissionRequest { /** * One or more permissions. */ PermissionRequest permission(@NonNull @PermissionDef String... permissions); /** * One or more permissions group. * * @param groups use constants in {@link Permission.Group}. */ PermissionRequest permission(@NonNull String[]... groups); /** * Set request rationale. */ PermissionRequest rationale(@NonNull Rationale> rationale); /** * Action to be taken when all permissions are granted. */ PermissionRequest onGranted(@NonNull Action> granted); /** * Action to be taken when all permissions are denied. */ PermissionRequest onDenied(@NonNull Action> denied); /** * Request permission. */ void start(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/Runtime.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; import androidx.annotation.NonNull; import com.yanzhenjie.permission.runtime.option.RuntimeOption; import com.yanzhenjie.permission.runtime.setting.AllRequest; import com.yanzhenjie.permission.runtime.setting.SettingRequest; import com.yanzhenjie.permission.source.Source; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Created by Zhenjie Yan on 2018/5/2. */ public class Runtime implements RuntimeOption { private static final String ADD_VOICEMAIL_MANIFEST = "android.permission.ADD_VOICEMAIL"; private static final PermissionRequestFactory FACTORY; private static List sAppPermissions; static { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { FACTORY = new MRequestFactory(); } else { FACTORY = new LRequestFactory(); } } public interface PermissionRequestFactory { /** * Create permission request. */ PermissionRequest create(Source source); } private Source mSource; public Runtime(Source source) { this.mSource = source; } @Override public PermissionRequest permission(@NonNull String... permissions) { checkPermissions(permissions); return FACTORY.create(mSource).permission(permissions); } @Override public PermissionRequest permission(@NonNull String[]... groups) { List permissionList = new ArrayList<>(); for (String[] group : groups) { checkPermissions(group); permissionList.addAll(Arrays.asList(group)); } String[] permissions = permissionList.toArray(new String[0]); return permission(permissions); } @Override public SettingRequest setting() { return new AllRequest(mSource); } /** * Check if the permissions are valid and each permission has been registered in manifest.xml. This method will * throw a exception if permissions are invalid or there is any permission which is not registered in manifest.xml. * * @param permissions permissions which will be checked. */ private void checkPermissions(String... permissions) { if (sAppPermissions == null) { sAppPermissions = new ArrayList<>(getManifestPermissions(mSource.getContext())); if (sAppPermissions.contains(ADD_VOICEMAIL_MANIFEST)) { sAppPermissions.add(Permission.ADD_VOICEMAIL); } } if (permissions.length == 0) { throw new IllegalArgumentException("Please enter at least one permission."); } for (String target : permissions) { if (!sAppPermissions.contains(target)) { throw new IllegalStateException(String.format("The permission %1$s is not registered in manifest.xml", target)); } } } /** * Get a list of permissions in the manifest. */ public static List getManifestPermissions(Context context) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS); String[] permissions = packageInfo.requestedPermissions; if (permissions == null || permissions.length == 0) { throw new IllegalStateException("You did not register any permissions in the manifest.xml."); } return Arrays.asList(permissions); } catch (PackageManager.NameNotFoundException e) { throw new AssertionError("Package name cannot be found."); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/option/RuntimeOption.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime.option; import androidx.annotation.NonNull; import com.yanzhenjie.permission.runtime.Permission; import com.yanzhenjie.permission.runtime.PermissionDef; import com.yanzhenjie.permission.runtime.PermissionRequest; import com.yanzhenjie.permission.runtime.setting.SettingRequest; /** * Created by Zhenjie Yan on 2/22/19. */ public interface RuntimeOption { /** * One or more permissions. */ PermissionRequest permission(@NonNull @PermissionDef String... permissions); /** * One or more permission groups. * * @param groups use constants in {@link Permission.Group}. */ PermissionRequest permission(@NonNull String[]... groups); /** * Permission settings. */ SettingRequest setting(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/setting/AllRequest.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime.setting; import com.yanzhenjie.permission.source.Source; /** *

SettingRequest executor.

* Created by Zhenjie Yan on 2016/12/28. */ public class AllRequest implements SettingRequest { private Source mSource; public AllRequest(Source source) { this.mSource = source; } @Override public void start(int requestCode) { SettingPage setting = new SettingPage(mSource); setting.start(requestCode); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/setting/SettingPage.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime.setting; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.provider.Settings; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/4/30. */ public class SettingPage { private static final String MARK = Build.MANUFACTURER.toLowerCase(); private Source mSource; public SettingPage(Source source) { this.mSource = source; } /** * Start. * * @param requestCode this code will be returned in onActivityResult() when the activity exits. */ public void start(int requestCode) { Intent intent; if (MARK.contains("huawei")) { intent = huaweiApi(mSource.getContext()); } else if (MARK.contains("xiaomi")) { intent = xiaomiApi(mSource.getContext()); } else if (MARK.contains("oppo")) { intent = oppoApi(mSource.getContext()); } else if (MARK.contains("vivo")) { intent = vivoApi(mSource.getContext()); } else if (MARK.contains("meizu")) { intent = meizuApi(mSource.getContext()); } else { intent = defaultApi(mSource.getContext()); } try { mSource.startActivityForResult(intent, requestCode); } catch (Exception e) { intent = defaultApi(mSource.getContext()); mSource.startActivityForResult(intent, requestCode); } } private static Intent defaultApi(Context context) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.fromParts("package", context.getPackageName(), null)); return intent; } private static Intent huaweiApi(Context context) { Intent intent = new Intent(); intent.setClassName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private static Intent xiaomiApi(Context context) { Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); intent.putExtra("extra_pkgname", context.getPackageName()); if (hasActivity(context, intent)) return intent; intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); if (hasActivity(context, intent)) return intent; intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private static Intent vivoApi(Context context) { Intent intent = new Intent(); intent.putExtra("packagename", context.getPackageName()); intent.setClassName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.SoftPermissionDetailActivity"); if (hasActivity(context, intent)) return intent; intent.setClassName("com.iqoo.secure", "com.iqoo.secure.safeguard.SoftPermissionDetailActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private static Intent oppoApi(Context context) { Intent intent = new Intent(); intent.putExtra("packageName", context.getPackageName()); intent.setClassName("com.color.safecenter", "com.color.safecenter.permission.PermissionManagerActivity"); if (hasActivity(context, intent)) return intent; intent.setClassName("com.oppo.safe", "com.oppo.safe.permission.PermissionAppListActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private static Intent meizuApi(Context context) { Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); intent.putExtra("packageName", context.getPackageName()); intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity"); if (hasActivity(context, intent)) return intent; return defaultApi(context); } private static boolean hasActivity(Context context, Intent intent) { PackageManager packageManager = context.getPackageManager(); return packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/runtime/setting/SettingRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.runtime.setting; /** * Created by Zhenjie Yan on 2018/4/30. */ public interface SettingRequest { /** * Start the setup. */ void start(int requestCode); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/setting/Setting.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.setting; import android.os.Build; import com.yanzhenjie.permission.setting.write.LWriteRequestFactory; import com.yanzhenjie.permission.setting.write.MWriteRequestFactory; import com.yanzhenjie.permission.setting.write.WriteRequest; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 3/1/19. */ public class Setting { private static final SettingRequestFactory SETTING_REQUEST_FACTORY; static { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { SETTING_REQUEST_FACTORY = new MWriteRequestFactory(); } else { SETTING_REQUEST_FACTORY = new LWriteRequestFactory(); } } public interface SettingRequestFactory { WriteRequest create(Source source); } private Source mSource; public Setting(Source source) { this.mSource = source; } /** * Handle write system settings. */ public WriteRequest write() { return SETTING_REQUEST_FACTORY.create(mSource); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/BaseRequest.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.setting.write; import android.content.Context; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 2018/6/1. */ abstract class BaseRequest implements WriteRequest { private Source mSource; private Rationale mRationale = new Rationale() { @Override public void showRationale(Context context, Void data, RequestExecutor executor) { executor.execute(); } }; private Action mGranted; private Action mDenied; BaseRequest(Source source) { this.mSource = source; } @Override public final WriteRequest rationale(Rationale rationale) { this.mRationale = rationale; return this; } @Override public final WriteRequest onGranted(Action granted) { this.mGranted = granted; return this; } @Override public final WriteRequest onDenied(Action denied) { this.mDenied = denied; return this; } /** * Why permissions are required. */ final void showRationale(RequestExecutor executor) { mRationale.showRationale(mSource.getContext(), null, executor); } /** * Callback acceptance status. */ final void callbackSucceed() { if (mGranted != null) { mGranted.onAction(null); } } /** * Callback rejected state. */ final void callbackFailed() { if (mDenied != null) { mDenied.onAction(null); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/LWriteRequest.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.setting.write; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 3/1/19. */ public class LWriteRequest extends BaseRequest { public LWriteRequest(Source source) { super(source); } @Override public void start() { callbackSucceed(); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/LWriteRequestFactory.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.setting.write; import com.yanzhenjie.permission.setting.Setting; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 3/1/19. */ public class LWriteRequestFactory implements Setting.SettingRequestFactory { @Override public WriteRequest create(Source source) { return new LWriteRequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/MWriteRequest.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.setting.write; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.bridge.BridgeRequest; import com.yanzhenjie.permission.bridge.RequestManager; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 3/1/19. */ public class MWriteRequest extends BaseRequest implements RequestExecutor, BridgeRequest.Callback { private Source mSource; public MWriteRequest(Source source) { super(source); this.mSource = source; } @Override public void start() { if (mSource.canWriteSetting()) { callbackSucceed(); } else { showRationale(this); } } @Override public void execute() { BridgeRequest request = new BridgeRequest(mSource); request.setType(BridgeRequest.TYPE_WRITE_SETTING); request.setCallback(this); RequestManager.get().add(request); } @Override public void cancel() { callbackFailed(); } @Override public void onCallback() { if (mSource.canWriteSetting()) { callbackSucceed(); } else { callbackFailed(); } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/MWriteRequestFactory.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.setting.write; import com.yanzhenjie.permission.setting.Setting; import com.yanzhenjie.permission.source.Source; /** * Created by Zhenjie Yan on 3/1/19. */ public class MWriteRequestFactory implements Setting.SettingRequestFactory { @Override public WriteRequest create(Source source) { return new MWriteRequest(source); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/setting/write/WriteRequest.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.setting.write; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.Rationale; /** * Created by Zhenjie Yan on 3/1/19. */ public interface WriteRequest { /** * Set request rationale. */ WriteRequest rationale(Rationale rationale); /** * Action to be taken when all permissions are granted. */ WriteRequest onGranted(Action granted); /** * Action to be taken when all permissions are denied. */ WriteRequest onDenied(Action denied); /** * Start install. */ void start(); } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/source/ActivitySource.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.source; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Build; /** *

Context Wrapper.

* Created by Zhenjie Yan on 2017/5/1. */ public class ActivitySource extends Source { private Activity mActivity; public ActivitySource(Activity activity) { this.mActivity = activity; } @Override public Context getContext() { return mActivity; } @Override public void startActivity(Intent intent) { mActivity.startActivity(intent); } @Override public void startActivityForResult(Intent intent, int requestCode) { mActivity.startActivityForResult(intent, requestCode); } @Override public boolean isShowRationalePermission(String permission) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false; return mActivity.shouldShowRequestPermissionRationale(permission); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/source/ContextSource.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.source; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import java.lang.reflect.Method; /** *

Context Wrapper.

* Created by Zhenjie Yan on 2017/5/1. */ public class ContextSource extends Source { private Context mContext; public ContextSource(Context context) { this.mContext = context; } @Override public Context getContext() { return mContext; } @Override public void startActivity(Intent intent) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } @Override public void startActivityForResult(Intent intent, int requestCode) { throw new UnsupportedOperationException("Unsupported operation."); } @Override public boolean isShowRationalePermission(String permission) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false; PackageManager packageManager = mContext.getPackageManager(); Class pkManagerClass = packageManager.getClass(); try { Method method = pkManagerClass.getMethod("shouldShowRequestPermissionRationale", String.class); if (!method.isAccessible()) method.setAccessible(true); return (boolean)method.invoke(packageManager, permission); } catch (Exception ignored) { return false; } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/source/FragmentSource.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.source; import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.os.Build; /** *

android.app.Fragment Wrapper.

* Created by Zhenjie Yan on 2017/5/1. */ public class FragmentSource extends Source { private Fragment mFragment; public FragmentSource(Fragment fragment) { this.mFragment = fragment; } @Override public Context getContext() { return mFragment.getActivity(); } @Override public void startActivity(Intent intent) { mFragment.startActivity(intent); } @Override public void startActivityForResult(Intent intent, int requestCode) { mFragment.startActivityForResult(intent, requestCode); } @Override public boolean isShowRationalePermission(String permission) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false; return mFragment.shouldShowRequestPermissionRationale(permission); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/source/Source.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.source; import android.app.AppOpsManager; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.provider.Settings; import androidx.annotation.RequiresApi; import java.lang.reflect.Field; import java.lang.reflect.Method; /** *

The source of the request.

* Created by Zhenjie Yan on 2017/5/1. */ public abstract class Source { private static final int MODE_ASK = 4; private static final int MODE_COMPAT = 5; private static final String CHECK_OP_NO_THROW = "checkOpNoThrow"; private static final String OP_REQUEST_INSTALL_PACKAGES = "OP_REQUEST_INSTALL_PACKAGES"; private static final String OP_SYSTEM_ALERT_WINDOW = "OP_SYSTEM_ALERT_WINDOW"; private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION"; private static final String OP_ACCESS_NOTIFICATIONS = "OP_ACCESS_NOTIFICATIONS"; private static final String OP_WRITE_SETTINGS = "OP_WRITE_SETTINGS"; private int mTargetSdkVersion; private String mPackageName; private PackageManager mPackageManager; private AppOpsManager mAppOpsManager; private NotificationManager mNotificationManager; public abstract Context getContext(); public abstract void startActivity(Intent intent); public abstract void startActivityForResult(Intent intent, int requestCode); public abstract boolean isShowRationalePermission(String permission); public int getTargetSdkVersion() { if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { mTargetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; } return mTargetSdkVersion; } public String getPackageName() { if (mPackageName == null) { mPackageName = getContext().getApplicationContext().getPackageName(); } return mPackageName; } private PackageManager getPackageManager() { if (mPackageManager == null) { mPackageManager = getContext().getPackageManager(); } return mPackageManager; } @RequiresApi(api = Build.VERSION_CODES.KITKAT) private AppOpsManager getAppOpsManager() { if (mAppOpsManager == null) { mAppOpsManager = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); } return mAppOpsManager; } private NotificationManager getNotificationManager() { if (mNotificationManager == null) { mNotificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE); } return mNotificationManager; } public final boolean canRequestPackageInstalls() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return true; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (getTargetSdkVersion() < Build.VERSION_CODES.O) { return reflectionOps(OP_REQUEST_INSTALL_PACKAGES); } return getPackageManager().canRequestPackageInstalls(); } return true; } public final boolean canDrawOverlays() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Context context = getContext(); if (getTargetSdkVersion() >= Build.VERSION_CODES.M) { return Settings.canDrawOverlays(context); } return reflectionOps(OP_SYSTEM_ALERT_WINDOW); } return true; } public final boolean canNotify() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return getNotificationManager().areNotificationsEnabled(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return reflectionOps(OP_POST_NOTIFICATION); } else { return true; } } public final boolean canListenerNotification() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return reflectionOps(OP_ACCESS_NOTIFICATIONS); } Context context = getContext(); String flat = Settings.Secure.getString(context.getContentResolver(), "enabled_notification_listeners"); return flat != null && flat.contains(getPackageName()); } public final boolean canWriteSetting() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Context context = getContext(); if (getTargetSdkVersion() >= Build.VERSION_CODES.M) { return Settings.System.canWrite(context); } return reflectionOps(OP_WRITE_SETTINGS); } return true; } @RequiresApi(api = Build.VERSION_CODES.KITKAT) private boolean reflectionOps(String opFieldName) { int uid = getContext().getApplicationInfo().uid; try { Class appOpsClass = AppOpsManager.class; Method method = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class); Field opField = appOpsClass.getDeclaredField(opFieldName); int opValue = (int) opField.get(Integer.class); int result = (int) method.invoke(getAppOpsManager(), opValue, uid, getPackageName()); return result == AppOpsManager.MODE_ALLOWED || result == MODE_ASK || result == MODE_COMPAT; } catch (Throwable e) { return true; } } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/source/WrapperSource.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.source; import android.content.Context; import android.content.Intent; /** * Created by Zhenjie Yan on 2/22/19. */ public class WrapperSource extends Source { private Source mSource; public WrapperSource(Source source) { this.mSource = source; } @Override public Context getContext() { return mSource.getContext(); } @Override public void startActivity(Intent intent) { mSource.startActivity(intent); } @Override public void startActivityForResult(Intent intent, int requestCode) { mSource.startActivityForResult(intent, requestCode); } @Override public boolean isShowRationalePermission(String permission) { return mSource.isShowRationalePermission(permission); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/source/XFragmentSource.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.source; import android.content.Context; import android.content.Intent; import android.os.Build; import androidx.fragment.app.Fragment; /** *

android.support.v4.app.Fragment Wrapper.

* Created by Zhenjie Yan on 2017/5/1. */ public class XFragmentSource extends Source { private Fragment mFragment; public XFragmentSource(Fragment fragment) { this.mFragment = fragment; } @Override public Context getContext() { return mFragment.getContext(); } @Override public void startActivity(Intent intent) { mFragment.startActivity(intent); } @Override public void startActivityForResult(Intent intent, int requestCode) { mFragment.startActivityForResult(intent, requestCode); } @Override public boolean isShowRationalePermission(String permission) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false; return mFragment.shouldShowRequestPermissionRationale(permission); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/task/TaskExecutor.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.task; import android.app.Dialog; import android.content.Context; import android.os.AsyncTask; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Created by Zhenjie Yan on 2019-09-23. */ public abstract class TaskExecutor extends AsyncTask { private static Executor sExecutor = Executors.newSingleThreadExecutor(); private Dialog mDialog; public TaskExecutor(Context context) { this.mDialog = new WaitDialog(context); this.mDialog.setCancelable(false); } @Override protected final void onPreExecute() { if (!mDialog.isShowing()) { mDialog.show(); } } @Override protected final void onPostExecute(T t) { if (mDialog.isShowing()) { mDialog.dismiss(); } onFinish(t); } protected abstract void onFinish(T t); /** * Just call this method. */ public final void execute() { executeOnExecutor(sExecutor); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/task/WaitDialog.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.task; import android.content.Context; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatDialog; import com.yanzhenjie.permission.R; /** * Created by Zhenjie Yan on 2019-09-23. */ public class WaitDialog extends AppCompatDialog { public WaitDialog(@NonNull Context context) { super(context, R.style.Permission_Theme_Dialog_Wait); setContentView(R.layout.permission_dialog_wait); } } ================================================ FILE: permission/src/main/java/com/yanzhenjie/permission/util/StringUtils.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.util; /** * Created Zhenjie Yan on 2019-10-02. */ public class StringUtils { private static final String DIGITS_TEXT = "0123456789ABCDEF"; private static final char[] DIGITS_ARRAY = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; public static String textToHex(String text) { StringBuilder builder = new StringBuilder(); byte[] textBytes = text.getBytes(); int bit; for (int i = 0; i < textBytes.length; i++) { bit = (textBytes[i] & 0x0f0) >> 4; builder.append(DIGITS_ARRAY[bit]); bit = textBytes[i] & 0x0f; builder.append(DIGITS_ARRAY[bit]); } return builder.toString().trim(); } public static String hexToText(String hexText) { char[] hexArray = hexText.toCharArray(); byte[] hexBytes = new byte[hexText.length() / 2]; for (int i = 0; i < hexBytes.length; i++) { int n = DIGITS_TEXT.indexOf(hexArray[2 * i]) * 16; n += DIGITS_TEXT.indexOf(hexArray[2 * i + 1]); hexBytes[i] = (byte) (n & 0xff); } return new String(hexBytes); } } ================================================ FILE: permission/src/main/res/drawable/permission_shape_wait_background.xml ================================================ ================================================ FILE: permission/src/main/res/layout/permission_dialog_wait.xml ================================================ ================================================ FILE: permission/src/main/res/values/dimens.xml ================================================ 3dp 5dp 10dp 15dp 20dp 25dp 30dp 35dp 40dp 12sp 14sp 16sp 18sp 20sp ================================================ FILE: permission/src/main/res/values/string.xml ================================================ Calendar Camera Accounts/Contacts Location Microphone Phone Call Log Body Sensors Activity Recognition SMS Storage Requesting permissions… ================================================ FILE: permission/src/main/res/values/style.xml ================================================ ================================================ FILE: permission/src/main/res/values-v21/style.xml ================================================ ================================================ FILE: permission/src/main/res/values-zh/string.xml ================================================ 日历 相机 手机账号/通讯录 位置信息 麦克风 电话 通话记录 身体传感器 健身运动 短信 存储空间 正在请求授权… ================================================ FILE: permission/src/main/res/values-zh-RHK/string.xml ================================================ 日曆 相機 手機賬號/通訊錄 位置信息 麥克風 電話 通話記錄 身體傳感器 健身運動 短訊 存儲空間 正在請求授權… ================================================ FILE: permission/src/main/res/values-zh-RTW/string.xml ================================================ 日曆 相機 手機賬號/通訊錄 位置信息 麥克風 電話 通話記錄 身體感測器 健身運動 簡訊 存儲空間 正在請求授權… ================================================ FILE: permission/src/main/res/xml/permission_file_paths.xml ================================================ ================================================ FILE: sample/.gitignore ================================================ /build ================================================ FILE: sample/build.gradle ================================================ apply plugin: rootProject.ext.plugins.application android { compileSdkVersion rootProject.ext.android.compileSdkVersion buildToolsVersion rootProject.ext.android.buildToolsVersion defaultConfig { applicationId rootProject.ext.android.applicationId minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion versionCode rootProject.ext.android.versionCode versionName rootProject.ext.android.versionName } } dependencies { implementation project(':permission') implementation rootProject.ext.dependencies.fragment } ================================================ FILE: sample/proguard-rules.pro ================================================ -optimizationpasses 5 -ignorewarnings -dontusemixedcaseclassnames -dontskipnonpubliclibraryclasses -dontskipnonpubliclibraryclassmembers -useuniqueclassmembernames -allowaccessmodification -dontpreverify -verbose -dontoptimize -renamesourcefileattribute SourceFile -keepattributes SourceFile,LineNumberTable -keepattributes Signature -keepattributes *Annotation* -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -keep public class **.R$*{ public static final int *; } -keepclassmembers class **.R$* { public static ; } -keepclassmembers class * { native ; } -keepclasseswithmembernames class * { native ; } -keepclasseswithmembers class * { public (android.content.Context); public (android.content.Context, android.util.AttributeSet); public (android.content.Context, android.util.AttributeSet, int); public void set*(***); public *** set*(***); public *** get*(***); public *** get*(); } -keep class android.support.annotation.Keep -keep @android.support.annotation.Keep class * {*;} -keepclasseswithmembers class * { @android.support.annotation.Keep ; } -keepclasseswithmembers class * { @android.support.annotation.Keep ; } -keepclasseswithmembers class * { @android.support.annotation.Keep (...); } -keep public class * implements android.os.Parcelable{*;} -keepclasseswithmembers class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } -keep public class android.support.v7.widget.SearchView{*;} -keep public class * extends android.support.v4.view.ActionProvider{*;} ================================================ FILE: sample/src/main/AndroidManifest.xml ================================================ ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/App.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample; import android.app.Application; import android.view.View; import com.yanzhenjie.permission.sample.widget.AlertWindow; import com.yanzhenjie.permission.sample.widget.LauncherView; /** * Created by Zhenjie Yan on 2018/5/30. */ public class App extends Application { private static App _instance; @Override public void onCreate() { super.onCreate(); _instance = this; } public static App get() { return _instance; } public void showLauncherView() { final AlertWindow alertWindow = new AlertWindow(this); LauncherView view = new LauncherView(this); view.setCancelClickListener(new View.OnClickListener() { @Override public void onClick(View v) { alertWindow.dismiss(); } }); alertWindow.setContentView(view); alertWindow.show(); } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/InstallRationale.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import java.io.File; /** * Created by Zhenjie Yan on 2018/4/29. */ public class InstallRationale implements Rationale { @Override public void showRationale(Context context, File data, final RequestExecutor executor) { new AlertDialog.Builder(context).setCancelable(false) .setTitle(R.string.title_dialog) .setMessage(R.string.message_install_failed) .setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.execute(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.cancel(); } }) .show(); } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/NotifyListenerRationale.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; /** * Created by Zhenjie Yan on 2018/5/30. */ public class NotifyListenerRationale implements Rationale { @Override public void showRationale(Context context, Void data, final RequestExecutor executor) { new AlertDialog.Builder(context).setCancelable(false) .setTitle(R.string.title_dialog) .setMessage(R.string.message_notification_listener_rationale) .setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.execute(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.cancel(); } }) .show(); } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/NotifyRationale.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; /** * Created by Zhenjie Yan on 2018/5/30. */ public class NotifyRationale implements Rationale { @Override public void showRationale(Context context, Void data, final RequestExecutor executor) { new AlertDialog.Builder(context).setCancelable(false) .setTitle(R.string.title_dialog) .setMessage(R.string.message_notification_rationale) .setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.execute(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.cancel(); } }) .show(); } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/OverlayRationale.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; /** * Created by Zhenjie Yan on 2018/5/30. */ public class OverlayRationale implements Rationale { @Override public void showRationale(Context context, Void data, final RequestExecutor executor) { new AlertDialog.Builder(context).setCancelable(false) .setTitle(R.string.title_dialog) .setMessage(R.string.message_overlay_failed) .setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.execute(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.cancel(); } }) .show(); } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/RuntimeRationale.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.text.TextUtils; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; import com.yanzhenjie.permission.runtime.Permission; import java.util.List; /** * Created by Zhenjie Yan on 2018/1/1. */ public final class RuntimeRationale implements Rationale> { @Override public void showRationale(Context context, List permissions, final RequestExecutor executor) { List permissionNames = Permission.transformText(context, permissions); String message = context.getString(R.string.message_permission_rationale, TextUtils.join("\n", permissionNames)); new AlertDialog.Builder(context).setCancelable(false) .setTitle(R.string.title_dialog) .setMessage(message) .setPositiveButton(R.string.resume, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.execute(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.cancel(); } }) .show(); } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/WriteSettingRationale.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import com.yanzhenjie.permission.Rationale; import com.yanzhenjie.permission.RequestExecutor; /** * Created by Zhenjie Yan on 3/1/19. */ public class WriteSettingRationale implements Rationale { @Override public void showRationale(Context context, Void data, final RequestExecutor executor) { new AlertDialog.Builder(context).setCancelable(false) .setTitle(R.string.title_dialog) .setMessage(R.string.message_write_setting_failed) .setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.execute(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { executor.cancel(); } }) .show(); } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/app/MainActivity.java ================================================ /* * Copyright © Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample.app; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.Toolbar; import com.yanzhenjie.permission.Action; import com.yanzhenjie.permission.AndPermission; import com.yanzhenjie.permission.runtime.Permission; import com.yanzhenjie.permission.runtime.PermissionDef; import com.yanzhenjie.permission.sample.App; import com.yanzhenjie.permission.sample.InstallRationale; import com.yanzhenjie.permission.sample.NotifyListenerRationale; import com.yanzhenjie.permission.sample.NotifyRationale; import com.yanzhenjie.permission.sample.OverlayRationale; import com.yanzhenjie.permission.sample.R; import com.yanzhenjie.permission.sample.RuntimeRationale; import com.yanzhenjie.permission.sample.WriteSettingRationale; import com.yanzhenjie.permission.sample.util.FileUtils; import com.yanzhenjie.permission.sample.util.IOUtils; import com.yanzhenjie.permission.task.TaskExecutor; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; /** * Created by Zhenjie Yan on 2016/9/17. */ public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final int REQUEST_CODE_SETTING = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); findViewById(R.id.btn_request_camera).setOnClickListener(this); findViewById(R.id.btn_request_contact).setOnClickListener(this); findViewById(R.id.btn_request_location).setOnClickListener(this); findViewById(R.id.btn_request_calendar).setOnClickListener(this); findViewById(R.id.btn_request_microphone).setOnClickListener(this); findViewById(R.id.btn_request_storage).setOnClickListener(this); findViewById(R.id.btn_request_phone).setOnClickListener(this); findViewById(R.id.btn_request_call_log).setOnClickListener(this); findViewById(R.id.btn_request_sensors).setOnClickListener(this); findViewById(R.id.btn_request_activity_recognition).setOnClickListener(this); findViewById(R.id.btn_request_sms).setOnClickListener(this); findViewById(R.id.btn_setting).setOnClickListener(this); findViewById(R.id.btn_notification).setOnClickListener(this); findViewById(R.id.btn_notification_listener).setOnClickListener(this); findViewById(R.id.btn_install).setOnClickListener(this); findViewById(R.id.btn_overlay).setOnClickListener(this); findViewById(R.id.btn_write_setting).setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_request_camera: { requestPermission(Permission.Group.CAMERA); break; } case R.id.btn_request_contact: { PopupMenu popupMenu = createMenu(v, getResources().getStringArray(R.array.contacts)); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int order = item.getItemId(); switch (order) { case 0: { requestPermission(Permission.READ_CONTACTS); break; } case 1: { requestPermission(Permission.WRITE_CONTACTS); break; } case 2: { requestPermission(Permission.GET_ACCOUNTS); break; } case 3: { requestPermission(Permission.Group.CONTACTS); break; } } return true; } }); popupMenu.show(); break; } case R.id.btn_request_location: { PopupMenu popupMenu = createMenu(v, getResources().getStringArray(R.array.location)); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int order = item.getItemId(); switch (order) { case 0: { requestPermission(Permission.ACCESS_FINE_LOCATION); break; } case 1: { requestPermission(Permission.ACCESS_COARSE_LOCATION); break; } case 2: { requestPermission(Permission.Group.LOCATION); break; } } return true; } }); popupMenu.show(); break; } case R.id.btn_request_calendar: { PopupMenu popupMenu = createMenu(v, getResources().getStringArray(R.array.calendar)); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int order = item.getOrder(); switch (order) { case 0: { requestPermission(Permission.READ_CALENDAR); break; } case 1: { requestPermission(Permission.WRITE_CALENDAR); break; } case 2: { requestPermission(Permission.Group.CALENDAR); break; } } return true; } }); popupMenu.show(); break; } case R.id.btn_request_microphone: { requestPermission(Permission.Group.MICROPHONE); break; } case R.id.btn_request_storage: { PopupMenu popupMenu = createMenu(v, getResources().getStringArray(R.array.storage)); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int order = item.getOrder(); switch (order) { case 0: { requestPermission(Permission.READ_EXTERNAL_STORAGE); break; } case 1: { requestPermission(Permission.WRITE_EXTERNAL_STORAGE); break; } case 2: { requestPermission(Permission.Group.STORAGE); break; } } return true; } }); popupMenu.show(); break; } case R.id.btn_request_phone: { PopupMenu popupMenu = createMenu(v, getResources().getStringArray(R.array.phone)); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int order = item.getOrder(); switch (order) { case 0: { requestPermission(Permission.READ_PHONE_STATE); break; } case 1: { requestPermission(Permission.CALL_PHONE); break; } case 2: { requestPermission(Permission.READ_PHONE_NUMBERS); break; } case 3: { requestPermission(Permission.ANSWER_PHONE_CALLS); break; } case 4: { requestPermission(Permission.USE_SIP); break; } case 5: { requestPermission(Permission.ADD_VOICEMAIL); break; } case 6: { // ADD_VOICEMAIL is special, not shown here. requestPermission(Permission.READ_PHONE_STATE, Permission.CALL_PHONE, Permission.READ_PHONE_NUMBERS, Permission.ANSWER_PHONE_CALLS, Permission.USE_SIP); break; } } return true; } }); popupMenu.show(); break; } case R.id.btn_request_call_log: { PopupMenu popupMenu = createMenu(v, getResources().getStringArray(R.array.call_log)); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int order = item.getOrder(); switch (order) { case 0: { requestPermission(Permission.READ_CALL_LOG); break; } case 1: { requestPermission(Permission.WRITE_CALL_LOG); break; } case 2: { requestPermission(Permission.PROCESS_OUTGOING_CALLS); break; } case 3: { requestPermission(Permission.Group.CALL_LOG); break; } } return true; } }); popupMenu.show(); break; } case R.id.btn_request_sensors: { requestPermission(Permission.Group.SENSORS); break; } case R.id.btn_request_activity_recognition: { requestPermission(Permission.Group.ACTIVITY_RECOGNITION); break; } case R.id.btn_request_sms: { PopupMenu popupMenu = createMenu(v, getResources().getStringArray(R.array.sms)); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { int order = item.getOrder(); switch (order) { case 0: { requestPermission(Permission.SEND_SMS); break; } case 1: { requestPermission(Permission.RECEIVE_SMS); break; } case 2: { requestPermission(Permission.READ_SMS); break; } case 3: { requestPermission(Permission.RECEIVE_WAP_PUSH); break; } case 4: { requestPermission(Permission.RECEIVE_MMS); break; } case 5: { requestPermission(Permission.Group.SMS); break; } } return true; } }); popupMenu.show(); break; } case R.id.btn_setting: { setPermission(); break; } case R.id.btn_notification: { requestNotification(); break; } case R.id.btn_notification_listener: { requestNotificationListener(); break; } case R.id.btn_install: { requestPermissionForInstallPackage(); break; } case R.id.btn_overlay: { requestPermissionForAlertWindow(); break; } case R.id.btn_write_setting: { requestWriteSystemSetting(); break; } } } /** * Request permissions. */ private void requestPermission(@PermissionDef String... permissions) { AndPermission.with(this) .runtime() .permission(permissions) .rationale(new RuntimeRationale()) .onGranted(new Action>() { @Override public void onAction(List permissions) { toast(R.string.successfully); } }) .onDenied(new Action>() { @Override public void onAction(@NonNull List permissions) { toast(R.string.failure); if (AndPermission.hasAlwaysDeniedPermission(MainActivity.this, permissions)) { showSettingDialog(MainActivity.this, permissions); } } }) .start(); } /** * Display setting dialog. */ public void showSettingDialog(Context context, final List permissions) { List permissionNames = Permission.transformText(context, permissions); String message = context.getString(R.string.message_permission_always_failed, TextUtils.join("\n", permissionNames)); new AlertDialog.Builder(context).setCancelable(false) .setTitle(R.string.title_dialog) .setMessage(message) .setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { setPermission(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .show(); } /** * Set permissions. */ private void setPermission() { AndPermission.with(this).runtime().setting().start(REQUEST_CODE_SETTING); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_CODE_SETTING: { Toast.makeText(MainActivity.this, R.string.message_setting_comeback, Toast.LENGTH_SHORT).show(); break; } } } /** * Request notification permission. */ private void requestNotification() { AndPermission.with(this) .notification() .permission() .rationale(new NotifyRationale()) .onGranted(new Action() { @Override public void onAction(Void data) { toast(R.string.successfully); } }) .onDenied(new Action() { @Override public void onAction(Void data) { toast(R.string.failure); } }) .start(); } /** * Request notification listener. */ private void requestNotificationListener() { AndPermission.with(this) .notification() .listener() .rationale(new NotifyListenerRationale()) .onGranted(new Action() { @Override public void onAction(Void data) { toast(R.string.successfully); } }) .onDenied(new Action() { @Override public void onAction(Void data) { toast(R.string.failure); } }) .start(); } /** * Request to read and write external storage permissions. */ private void requestPermissionForInstallPackage() { if (!FileUtils.externalAvailable()) { new AlertDialog.Builder(this) .setTitle(R.string.title_dialog) .setMessage(R.string.message_error_storeage_inavailable) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .show(); return; } AndPermission.with(this) .runtime() .permission(Permission.Group.STORAGE) .rationale(new RuntimeRationale()) .onGranted(new Action>() { @Override public void onAction(List data) { writeApkForInstallPackage(); } }) .onDenied(new Action>() { @Override public void onAction(List data) { toast(R.string.message_install_failed); } }) .start(); } private void writeApkForInstallPackage() { new TaskExecutor(MainActivity.this) { @Override protected File doInBackground(Void... voids) { try { InputStream input = getAssets().open("android.apk"); File apk = new File(FileUtils.getExternalDir(App.get(), Environment.DIRECTORY_DOWNLOADS), "AndPermission.apk"); if (apk.exists()) return apk; OutputStream output = new BufferedOutputStream(new FileOutputStream(apk)); IOUtils.write(input, output); IOUtils.close(output); return apk; } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onFinish(File apkFile) { if (apkFile == null) { new AlertDialog.Builder(MainActivity.this) .setTitle(R.string.title_dialog) .setMessage(R.string.message_error_save_failed) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .show(); } else { installPackage(apkFile); } } }.execute(); } /** * Install package. */ private void installPackage(File apkFile) { AndPermission.with(this) .install() .file(apkFile) .rationale(new InstallRationale()) .onGranted(new Action() { @Override public void onAction(File data) { // Installing. } }) .onDenied(new Action() { @Override public void onAction(File data) { // The user refused to install. } }) .start(); } private void requestPermissionForAlertWindow() { AndPermission.with(this).overlay().rationale(new OverlayRationale()).onGranted(new Action() { @Override public void onAction(Void data) { showAlertWindow(); } }).onDenied(new Action() { @Override public void onAction(Void data) { toast(R.string.message_overlay_failed); } }).start(); } private void requestWriteSystemSetting() { AndPermission.with(this).setting().write().rationale(new WriteSettingRationale()).onGranted(new Action() { @Override public void onAction(Void data) { toast(R.string.successfully); } }).onDenied(new Action() { @Override public void onAction(Void data) { toast(R.string.failure); } }).start(); } private void showAlertWindow() { App.get().showLauncherView(); Intent backHome = new Intent(Intent.ACTION_MAIN); backHome.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); backHome.addCategory(Intent.CATEGORY_HOME); startActivity(backHome); } /** * Create menu. */ private PopupMenu createMenu(View v, String[] menuArray) { PopupMenu popupMenu = new PopupMenu(this, v); Menu menu = popupMenu.getMenu(); for (int i = 0; i < menuArray.length; i++) { String menuText = menuArray[i]; menu.add(0, i, i, menuText); } return popupMenu; } protected void toast(@StringRes int message) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/app/NotifyListenerService.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample.app; import android.os.Build; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import androidx.annotation.RequiresApi; /** * Created by Zhenjie Yan on 2/14/19. */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public class NotifyListenerService extends NotificationListenerService { @Override public void onNotificationPosted(StatusBarNotification sbn) { } @Override public void onNotificationRemoved(StatusBarNotification sbn) { } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/util/FileUtils.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample.util; import android.content.Context; import android.os.Environment; import android.text.TextUtils; import androidx.annotation.Nullable; import java.io.File; /** * Created Zhenjie Yan on 2019-10-10. */ public class FileUtils { public static File getFileDir(Context context) { return getFileDir(context, null); } public static File getFileDir(Context context, @Nullable String type) { File root = context.getFilesDir(); if (TextUtils.isEmpty(type)) { return root; } else { File dir = new File(root, type); createDir(dir); return dir; } } public static boolean externalAvailable() { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } public static File getExternalDir(Context context) { return getExternalDir(context, null); } public static File getExternalDir(Context context, @Nullable String type) { if (externalAvailable()) { if (TextUtils.isEmpty(type)) { return context.getExternalFilesDir(null); } File dir = context.getExternalFilesDir(type); if (dir == null) { dir = context.getExternalFilesDir(null); dir = new File(dir, type); createDir(dir); } return dir; } throw new RuntimeException("External storage device is not available."); } public static File getRootDir() { return getRootDir(null); } public static File getRootDir(@Nullable String type) { if (externalAvailable()) { File root = Environment.getExternalStorageDirectory(); File appRoot = new File(root, "AndPermission"); createDir(appRoot); if (TextUtils.isEmpty(type)) { return appRoot; } File dir = new File(appRoot, type); createDir(dir); return dir; } throw new RuntimeException("External storage device is not available."); } public static void createDir(File dir) { if (dir.exists()) { if (!dir.isDirectory()) { dir.delete(); } } if (!dir.exists()) { dir.mkdirs(); } } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/util/IOUtils.java ================================================ /* * Copyright 2019 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample.util; import java.io.Closeable; import java.io.Flushable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Created Zhenjie Yan on 2019-10-10. */ public class IOUtils { public static void write(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[2048]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); out.flush(); } } public static void close(Closeable closeable) { try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } public static void flush(Flushable flushable) { try { flushable.flush(); } catch (IOException e) { e.printStackTrace(); } } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/widget/AlertWindow.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample.widget; import android.content.Context; import android.graphics.PixelFormat; import android.os.Build; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; /** * Created by Zhenjie Yan on 2018/5/30. */ public class AlertWindow { private Context mContext; private WindowManager mWindowManager; private WindowManager.LayoutParams mParams; private View mContentView; private boolean isShowing; public AlertWindow(Context context) { this.mContext = context.getApplicationContext(); this.create(); } private void create() { this.mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); this.mParams = new WindowManager.LayoutParams(); mParams.packageName = mContext.getPackageName(); mParams.width = WindowManager.LayoutParams.MATCH_PARENT; mParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; int overlay = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; int alertWindow = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; mParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? overlay : alertWindow; mParams.format = PixelFormat.RGBA_8888; mParams.gravity = Gravity.TOP; } /** * Set the view to show. * * @param layoutId target layout id. */ public void setContentView(int layoutId) { setContentView(LayoutInflater.from(mContext).inflate(layoutId, null, false)); } /** * Set the view to show. * * @param view target view. */ public void setContentView(View view) { this.mContentView = view; ViewGroup.LayoutParams params = view.getLayoutParams(); if (params == null) { params = new ViewGroup.LayoutParams(-1, -2); } else { params.width = -1; params.height = -2; } this.mContentView.setLayoutParams(params); } /** * AlertWindow is displayed. * * @return true, otherwise is false. */ public boolean isShowing() { return isShowing; } /** * Display the alert window. */ public void show() { if (isShowing) { Log.w("AlertWindow", "AlertWindow is already displayed."); } else { isShowing = true; mWindowManager.addView(mContentView, mParams); } } /** * Dismiss the */ public void dismiss() { if (!isShowing) { Log.w("AlertWindow", "AlertWindow is not displayed."); } else if (mContentView != null) { isShowing = false; mWindowManager.removeViewImmediate(mContentView); } } } ================================================ FILE: sample/src/main/java/com/yanzhenjie/permission/sample/widget/LauncherView.java ================================================ /* * Copyright 2018 Zhenjie Yan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yanzhenjie.permission.sample.widget; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.RelativeLayout; import com.yanzhenjie.permission.sample.R; /** * Created by Zhenjie Yan on 2018/5/30. */ public class LauncherView extends RelativeLayout implements View.OnClickListener { private View.OnClickListener mCancelClickListener; public LauncherView(Context context) { this(context, null, 0); } public LauncherView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LauncherView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); inflate(context, R.layout.window_launcher, this); findViewById(R.id.btn_cancel).setOnClickListener(this); } @Override public void onClick(View v) { int id = v.getId(); switch (id) { case R.id.btn_cancel: { if (mCancelClickListener != null) { mCancelClickListener.onClick(v); } break; } } } public void setCancelClickListener(OnClickListener cancelClickListener) { this.mCancelClickListener = cancelClickListener; } } ================================================ FILE: sample/src/main/res/layout/activity_main.xml ================================================