diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d060926 --- /dev/null +++ b/.gitignore @@ -0,0 +1,77 @@ +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +# *.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/caches + +# Keystore files +# Uncomment the following line if you do not want to check your keystore files in. +*.jks +*.gpg + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Package Files # +*.jar +*.war +*.ear + +# Other Files # +.DS_Store +/.idea/ +/gradle/ +/javadoc/ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..4859bb8 --- /dev/null +++ b/build.gradle @@ -0,0 +1,28 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.1.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.+' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..743d692 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,13 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..f865c72 --- /dev/null +++ b/local.properties @@ -0,0 +1,9 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Sun Jun 24 23:40:13 SGT 2018 +ndk.dir=D\:\\.android\\sdk\\ndk-bundle +sdk.dir=D\:\\.android\\sdk diff --git a/permission-jedi/build.gradle b/permission-jedi/build.gradle new file mode 100644 index 0000000..97168ff --- /dev/null +++ b/permission-jedi/build.gradle @@ -0,0 +1,44 @@ +apply plugin: 'com.android.library' + +ext { + _compileSdkVersion = 27 + _minSdkVersion = 16 + _targetSdkVersion = 27 + _versionCode = 3 + _versionName = "1.0.2" + _supportLibrary = '27.1.1' +} + +android { + + compileSdkVersion _compileSdkVersion + + defaultConfig { + minSdkVersion _minSdkVersion + targetSdkVersion _targetSdkVersion + versionCode _versionCode + versionName _versionName + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation "com.android.support:support-annotations:$_supportLibrary" + implementation "com.android.support:appcompat-v7:$_supportLibrary" +} + +// Publish to Maven Central via OSSRH Maven repository +// apply from: 'publish-maven.gradle' +// Publish to Maven Central + JCenter via Bintray + apply from: 'publish-bintray.gradle' \ No newline at end of file diff --git a/permission-jedi/gradle.properties b/permission-jedi/gradle.properties new file mode 100644 index 0000000..7260f5a --- /dev/null +++ b/permission-jedi/gradle.properties @@ -0,0 +1,31 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# GPG Signing +signing.keyId= +signing.password= +signing.secretKeyRingFile=secring.gpg + +# Sonatype OSSRH +ossrhUsername= +ossrhPassword= + +# Bintray +bintrayUser= +bintrayAPIKey= \ No newline at end of file diff --git a/permission-jedi/proguard-rules.pro b/permission-jedi/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/permission-jedi/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/permission-jedi/publish-bintray.gradle b/permission-jedi/publish-bintray.gradle new file mode 100644 index 0000000..83d1dc3 --- /dev/null +++ b/permission-jedi/publish-bintray.gradle @@ -0,0 +1,148 @@ +/** + * Publish to JCentral+Maven Central via Bintray + * + * Prework for Bintray + * 1. Register to Bintray and set up auto-signing: + * a) Generate yourself a keypair, if you don’t have one. + * https://help.github.com/articles/generating-a-new-gpg-key/ + * https://central.sonatype.org/pages/working-with-pgp-signatures.html + * b) Add GPG keypair to your profile at tab GPGUserSigning + * https://bintray.com/profile/edit + * 2) Add your Sonatype account under “accounts”. + * a) Create your Sonatype account under “accounts” + * b) Setup your default Maven repo with your GPG key signing + * https://central.sonatype.org/pages/ossrh-guide.html#initial-setup + * 3. Create and link your package: + * a) Import from a GitHub repo(or create a new Maven package) + * b) Click on “Add to JCenter” + * 4. Set up Maven to deploy to Bintray by using pom.xml snippets from “Set me up!” guide, or use the bintray-gradle-plugin. + * + * Deploying via Bintray with Gradle refer: + * https://medium.com/@yegor_zatsepin/simple-way-to-publish-your-android-library-to-jcenter-d1e145bacf1 + */ + +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' +apply plugin: 'signing' + +def POM_NAME = 'permission-jedi' +def POM_ARTIFACT_ID = 'permission-jedi' +def POM_GROUP_ID = 'com.github.kopihao' +def POM_VERSION = project._versionName +def POM_DESCRIPTION = POM_GROUP_ID + ':' + POM_ARTIFACT_ID + ':' + POM_VERSION +def POM_URL = 'https://github.com/kopihao/permission-jedi' +def POM_GIT_URL = 'https://github.com/kopihao/permission-jedi.git' +def POM_CONNECTION = 'scm:git@github.com:kopihao/permission-jedi.git' +def POM_LICENSE_ALL = ["Apache-2.0"] +def POM_LICENSE_NAME = 'The Apache Software License, Version 2.0' +def POM_LICENSE_URL = 'http://www.apache.org/licenses/LICENSE-2.0.txt' +def POM_DEVELOPER_ID = 'kopihao.my' +def POM_DEVELOPER_NAME = 'Kopihao' +def POM_DEVELOPER_EMAIL = 'kopihao@gmail.com' +def POM_PACKAGING = 'aar' + +group = POM_GROUP_ID +version = POM_VERSION +println 'Uploading ' + POM_DESCRIPTION + " via Bintray" + +install { + repositories.mavenInstaller { + pom.project { + name POM_NAME + packaging POM_PACKAGING + groupId POM_GROUP_ID + artifactId POM_ARTIFACT_ID + description POM_DESCRIPTION + url POM_URL + scm { + url POM_URL + connection POM_CONNECTION + developerConnection POM_CONNECTION + } + licenses { + license { + name POM_LICENSE_NAME + url POM_LICENSE_URL + } + } + developers { + developer { + id POM_DEVELOPER_ID + name POM_DEVELOPER_NAME + email POM_DEVELOPER_EMAIL + } + } + } + } +} + +// Uncomment to use local.properties +//def propertiesFileName = 'local.properties' +//Properties properties = new Properties(); +//properties.load(project.rootProject.file(propertiesFileName).newDataInputStream()); + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} +artifacts { + archives javadocJar + archives sourcesJar +} +signing { + sign configurations.archives +} + +//signing { +// sign configurations.archives +//} + +/** + * kopihao: + * Beware when creating your android library module, + * your artifact name should identical as module name + * Otherwise, your library files and pom will be uploaded to different hierarchy. + * I realized only when I learn to check from the following: + * http://jcenter.bintray.com/com/jakewharton/butterknife + * http://jcenter.bintray.com/com/loopj/android/android-async-http + */ + +def BINTRAY_REPO = 'maven' +def BINTRAY_USER = bintrayUser +def BINTRAY_API_KEY = bintrayAPIKey +def BINTRAY_NAME = POM_ARTIFACT_ID +def BINTRAY_DESCRIPTION = POM_DESCRIPTION +def BINTRAY_URL = POM_URL +def BINTRAY_GIT_URL = POM_GIT_URL +def BINTRAY_LICENSE_ALL = POM_LICENSE_ALL + +bintray { + user = BINTRAY_USER + key = BINTRAY_API_KEY + configurations = ['archives'] + pkg { + repo = BINTRAY_REPO + name = BINTRAY_NAME + desc = BINTRAY_DESCRIPTION + websiteUrl = BINTRAY_URL + vcsUrl = BINTRAY_GIT_URL + licenses = BINTRAY_LICENSE_ALL + dryRun = false + publish = true + override = false + publicDownloadNumbers = true + version { + desc = BINTRAY_DESCRIPTION + } + } +} \ No newline at end of file diff --git a/permission-jedi/publish-maven.gradle b/permission-jedi/publish-maven.gradle new file mode 100644 index 0000000..7f051ef --- /dev/null +++ b/permission-jedi/publish-maven.gradle @@ -0,0 +1,129 @@ +/** + * Publish to Maven Central via OSSRH Maven repository + * OSSRH Central Repository requirement: + * https://central.sonatype.org/pages/requirements.html + * Deploying to OSSRH with Gradle refer: + * https://central.sonatype.org/pages/gradle.html#signing-artifacts + */ + +apply plugin: 'maven' +apply plugin: 'signing' + +// Uncomment codes below to publish Java Libraries +//def POM_PACKAGING = 'jar' +//task javadocJar(type: Jar, dependsOn: javadoc) { +// classifier = 'javadoc' +// from tasks.javadoc.destinationDir +//} +//task sourcesJar(type: Jar) { +// from sourceSets.main.allSource +// classifier = 'sources' +//} +//artifacts { +// archives jar +// archives javadocJar +// archives sourcesJar +//} +//signing { +// sign configurations.archives +//} + +// Uncomment codes below to publish Android Libraries +def POM_PACKAGING = 'aar' +task sourceJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier "source" +} +task javadoc(type: Javadoc) { + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + destinationDir = file("../javadoc/") + failOnError false +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from tasks.javadoc.destinationDir +} +//Creating sources with comments +task androidSourcesJar(type: Jar) { + classifier = 'sources' + from android.sourceSets.main.java.srcDirs +} +artifacts { + archives javadocJar + archives androidSourcesJar +} +signing { + sign configurations.archives +} + +def POM_NAME = 'permission-jedi' +def POM_ARTIFACT_ID = 'permission-jedi' +def POM_GROUP_ID = 'com.github.kopihao' +def POM_VERSION = project._versionName +def POM_DESCRIPTION = POM_GROUP_ID + ':' + POM_ARTIFACT_ID + ':' + POM_VERSION +def POM_URL = 'https://github.com/kopihao/permission-jedi' +def POM_CONNECTION = 'scm:git@github.com:kopihao/permission-jedi.git' +def POM_LICENSE_ALL = ["Apache-2.0"] +def POM_LICENSE_NAME = 'The Apache Software License, Version 2.0' +def POM_LICENSE_URL = 'http://www.apache.org/licenses/LICENSE-2.0.txt' +def POM_DEVELOPER_ID = 'kopihao.my' +def POM_DEVELOPER_NAME = 'Kopihao' +def POM_DEVELOPER_EMAIL = 'kopihao@gmail.com' + +group = POM_GROUP_ID +version = POM_VERSION +println 'Uploading ' + POM_DESCRIPTION + " via OSSRH" + +def Object getSonatypeUsername() { + String usn = hasProperty('ossrhUsername') ? findProperty('ossrhUsername') : 'ossrhUsername'; + println 'ossrhUsername : ' + usn.toString(); + return usn; +} + +def Object getSonatypePassword() { + String pwd = hasProperty('sonatypePassword') ? findProperty('ossrhPassword') : 'ossrhPassword'; + println 'ossrhPassword : ' + pwd.toString(); + return pwd; +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: getSonatypeUsername(), password: getSonatypePassword()) + } + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { + authentication(userName: getSonatypeUsername(), password: getSonatypePassword()) + } + pom.project { + name POM_NAME + packaging POM_PACKAGING + description POM_DESCRIPTION + artifactId POM_ARTIFACT_ID + url POM_URL + scm { + url POM_URL + connection POM_CONNECTION + developerConnection POM_CONNECTION + } + licenses { + license { + name POM_LICENSE_NAME + url POM_LICENSE_URL + distribution 'repo' + } + } + developers { + developer { + id POM_DEVELOPER_ID + name POM_DEVELOPER_NAME + email POM_DEVELOPER_EMAIL + } + } + } + } + } +} + diff --git a/permission-jedi/src/androidTest/java/com/kopirealm/permissionjedi/ExampleInstrumentedTest.java b/permission-jedi/src/androidTest/java/com/kopirealm/permissionjedi/ExampleInstrumentedTest.java new file mode 100644 index 0000000..91a5031 --- /dev/null +++ b/permission-jedi/src/androidTest/java/com/kopirealm/permissionjedi/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.kopirealm.permissionjedi; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.kopirealm.permissionjedi.test", appContext.getPackageName()); + } +} diff --git a/permission-jedi/src/main/AndroidManifest.xml b/permission-jedi/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9c24473 --- /dev/null +++ b/permission-jedi/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/permission-jedi/src/main/java/com/kopirealm/permissionjedi/PermissionJedi.java b/permission-jedi/src/main/java/com/kopirealm/permissionjedi/PermissionJedi.java new file mode 100644 index 0000000..bedae5a --- /dev/null +++ b/permission-jedi/src/main/java/com/kopirealm/permissionjedi/PermissionJedi.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2018 Kopihao + *

+ * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kopirealm.permissionjedi; + +import android.Manifest; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.text.TextUtils; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; + +public class PermissionJedi { + + private static PermissionJedi jedi = null; + + public static PermissionJedi getJedi() { + System.gc(); + jedi = (jedi != null) ? jedi : new PermissionJedi(); + return jedi; + } + + public static final String ACTION_CHECK = "ACTION_CHECK"; + public static final String ACTION_REQUEST = "ACTION_REQUEST"; + public static final String ACTION_APPSETTINGS = "ACTION_APPSETTINGS"; + + private Activity activity; + private PermissionJediActions actions; + private HashSet permissions = new HashSet<>(); + private boolean strictMode = false; + + public static PermissionJedi init(Activity activity) { + return PermissionJedi.getJedi().setActivity(activity); + } + + protected boolean isValidContext() { + if (activity == null || activity.isFinishing()) { + return false; + } + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { + if (activity.isDestroyed()) { + return false; + } + } + return true; + } + + protected boolean notifyPermissionStatus(final HashMap permits) { + try { + if (!isValidContext()) { + return false; + } + if (actions == null) { + return false; + } + new Handler(activity.getMainLooper()).post(new Runnable() { + @Override + public void run() { + actions.onPermissionReviewed(permits); + } + }); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + public PermissionJedi useStrictMode() { + this.strictMode = true; + return this; + } + + public PermissionJedi addPermissions(@NonNull String... permissions) { + this.permissions.addAll(Arrays.asList(permissions)); + return this; + } + + public void bind(PermissionJediActivity activity) { + activity.notifier = actions; + } + + public PermissionJedi setActions(PermissionJediActions actions) { + this.actions = actions; + return this; + } + + public PermissionJedi setActivity(Activity activity) { + this.activity = activity; + return this; + } + + public void check() { + if (permissions == null || permissions.isEmpty()) { + return; + } + execute(ACTION_CHECK); + } + + public void request() { + if (permissions == null || permissions.isEmpty()) { + return; + } + execute(ACTION_REQUEST); + } + + private void execute(String action) { + try { + final String[] request = permissions.toArray(new String[permissions.size()]); + if (strictMode && !hasValidPermissions()) { + throw new IllegalAndroidPermissionException(); + } + final Intent intent = new Intent(activity, PermissionJediActivity.class); + Bundle extras = new Bundle(); + extras.putString(PermissionJediActivity.EXTRA_ACTION, action); + extras.putStringArray(PermissionJediActivity.EXTRA_PERMISSIONS, request); + intent.putExtras(extras); + activity.startActivity(intent); + } catch (IllegalAndroidPermissionException e) { + e.printStackTrace(); + } + } + + public void gotoAppSettings() { + execute(ACTION_APPSETTINGS); + } + + public HashSet getAndroidPermissions() { + final HashSet androidPermissions = new HashSet<>(); + try { + for (Field field : Manifest.permission.class.getFields()) { + String permission = (String) field.get(""); + if (permission.startsWith("android.permission")) { + androidPermissions.add(permission); + } + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return androidPermissions; + } + + private boolean hasValidPermissions() { + final HashSet androidPermissions = getAndroidPermissions(); + final HashSet runtimePermissions = permissions; + if (!androidPermissions.isEmpty() && !androidPermissions.containsAll(runtimePermissions)) { + return false; + } + return true; + } + + private class IllegalAndroidPermissionException extends IllegalArgumentException { + public IllegalAndroidPermissionException() { + super("Illegal Android Permission Found."); + } + } + + public interface PermissionJediActions { + public void onPermissionReviewed(@NonNull HashMap permits); + } + + public void showRationaleDialog(@NonNull String rationale, @NonNull String btnPos, @NonNull String btnNeg, @NonNull DialogInterface.OnClickListener diPos, @NonNull DialogInterface.OnClickListener diNeg) { + customDialog(rationale, btnPos, btnNeg, diPos, diNeg).show(); + } + + public void showGotoSettingsDialog(@NonNull String rationale) { + rationale = (!TextUtils.isEmpty(rationale)) ? rationale : activity.getString(R.string.txt_permission_required); + customDialog( + rationale, + activity.getString(R.string.btn_go_now), + activity.getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + gotoAppSettings(); + } + }, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }).show(); + } + + public void showGotoSettingsDialog(@NonNull String rationale, @NonNull String btnPos, @NonNull String btnNeg, @NonNull DialogInterface.OnClickListener diPos, @NonNull DialogInterface.OnClickListener diNeg) { + customDialog( + rationale, + TextUtils.isEmpty(btnPos) ? activity.getString(R.string.btn_go_now) : btnPos, + TextUtils.isEmpty(btnNeg) ? activity.getString(R.string.btn_not_now) : btnNeg, + diPos, + diNeg).show(); + } + + private AlertDialog.Builder customDialog(@NonNull String rationale, @NonNull String btnPos, @NonNull String btnNeg, @NonNull DialogInterface.OnClickListener diPos, @NonNull DialogInterface.OnClickListener diNeg) { + AlertDialog.Builder adb = new AlertDialog.Builder(activity); + adb.setMessage(rationale); + if (!(TextUtils.isEmpty(btnPos) && diPos == null)) { + btnPos = TextUtils.isEmpty(btnPos) ? activity.getString(android.R.string.ok) : btnPos; + adb.setPositiveButton(btnPos, diPos); + } + if (!(TextUtils.isEmpty(btnNeg) && diNeg == null)) { + btnNeg = TextUtils.isEmpty(btnNeg) ? activity.getString(android.R.string.cancel) : btnNeg; + adb.setNegativeButton(btnNeg, diNeg); + } + adb.setCancelable(false); + return adb; + } + +} diff --git a/permission-jedi/src/main/java/com/kopirealm/permissionjedi/PermissionJediActivity.java b/permission-jedi/src/main/java/com/kopirealm/permissionjedi/PermissionJediActivity.java new file mode 100644 index 0000000..2b2ffa9 --- /dev/null +++ b/permission-jedi/src/main/java/com/kopirealm/permissionjedi/PermissionJediActivity.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2018 Kopihao + *

+ * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kopirealm.permissionjedi; + +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +public class PermissionJediActivity extends Activity { + + private static final int REQUEST_CODE_ASK_PERMISSIONS = 91001; + private static final int REQUEST_CODE_GOTO_SETTINGS = 91002; + public static final String EXTRA_ACTION = "EXTRA_ACTION"; + public static final String EXTRA_PERMISSIONS = "EXTRA_PERMISSIONS"; + + private Activity self = this; + private String action = ""; + private String[] permissions = null; + protected PermissionJedi.PermissionJediActions notifier = null; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle extras = getIntent().getExtras(); + action = extras.getString(EXTRA_ACTION, ""); + permissions = extras.getStringArray(EXTRA_PERMISSIONS); + PermissionJedi.getJedi().bind(this); + if (androidPreM()) { + grantAllPermissions(); + } else { + runtimePermissions(); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(EXTRA_ACTION, action); + outState.putStringArray(EXTRA_PERMISSIONS, permissions); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + if (savedInstanceState != null) { + action = savedInstanceState.getString(EXTRA_ACTION, ""); + permissions = savedInstanceState.getStringArray(EXTRA_PERMISSIONS); + } + Log.d("Jasper", "run onRestoreInstanceState()"); + } + + @Override + protected void onResume() { + super.onResume(); + if (!PermissionJedi.getJedi().isValidContext()) { + Log.d("Jasper", "new PermissionJedi()"); + finish(); + } + } + + private boolean androidPreM() { + return (Build.VERSION.SDK_INT < Build.VERSION_CODES.M); + } + + private void runtimePermissions() { + switch (action) { + case PermissionJedi.ACTION_CHECK: + hasPermissions(permissions); + break; + case PermissionJedi.ACTION_REQUEST: + requestPermissions(permissions); + break; + case PermissionJedi.ACTION_APPSETTINGS: + gotoAppSettings(); + break; + default: + finish(); + } + } + + private HashMap checkPermission(@NonNull String... permissions) { + HashMap permits = new HashMap<>(); + for (final String p : permissions) { + permits.put(p, (ContextCompat.checkSelfPermission(self, p) == PackageManager.PERMISSION_GRANTED)); + } + return permits; + } + + private void hasPermissions(@NonNull String... permissions) { + final HashMap permits = checkPermission(permissions); + PermissionJedi.getJedi().notifyPermissionStatus(permits); + finish(); + } + + private void requestPermissions(@NonNull String... permissions) { + ArrayList missingPermissions = new ArrayList(); + HashMap permits = checkPermission(permissions); + for (final String p : permissions) { + if (!permits.get(p)) { + missingPermissions.add(p); + } + } + if (missingPermissions.isEmpty()) { + grantAllPermissions(permissions); + } else { + ActivityCompat.requestPermissions(self, missingPermissions + .toArray(new String[missingPermissions.size()]), REQUEST_CODE_ASK_PERMISSIONS); + } + } + + private void grantAllPermissions(String... permissions) { + final int[] grantResults = new int[permissions.length]; + Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED); + onRequestPermissionsResult(REQUEST_CODE_ASK_PERMISSIONS, permissions, grantResults); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + final HashMap permits = new HashMap<>(); + if (requestCode == REQUEST_CODE_ASK_PERMISSIONS) { + for (int i = 0; i < permissions.length; i++) { + permits.put(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED); + } + } + PermissionJedi.getJedi().notifyPermissionStatus(permits); + finish(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_CODE_GOTO_SETTINGS) { + hasPermissions(permissions); + return; + } + } + + public void gotoAppSettings() { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", self.getPackageName(), null); + intent.setData(uri); + self.startActivityForResult(intent, REQUEST_CODE_GOTO_SETTINGS); + } + +} diff --git a/permission-jedi/src/main/res/values/dimens.xml b/permission-jedi/src/main/res/values/dimens.xml new file mode 100644 index 0000000..59a0b0c --- /dev/null +++ b/permission-jedi/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 16dp + diff --git a/permission-jedi/src/main/res/values/strings.xml b/permission-jedi/src/main/res/values/strings.xml new file mode 100644 index 0000000..6ac7052 --- /dev/null +++ b/permission-jedi/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + PermissionJedi + PermissionJediActivity + Permission is required. + Go Now + Not Now + + \ No newline at end of file diff --git a/permission-jedi/src/main/res/values/styles.xml b/permission-jedi/src/main/res/values/styles.xml new file mode 100644 index 0000000..c018c39 --- /dev/null +++ b/permission-jedi/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/permission-jedi/src/test/java/com/kopirealm/permissionjedi/ExampleUnitTest.java b/permission-jedi/src/test/java/com/kopirealm/permissionjedi/ExampleUnitTest.java new file mode 100644 index 0000000..84eb07f --- /dev/null +++ b/permission-jedi/src/test/java/com/kopirealm/permissionjedi/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.kopirealm.permissionjedi; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..5907c83 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':permission-jedi'