├── .gitignore ├── .idea ├── encodings.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── Screenshot_2019-05-02-18-04-40-864_cn.bavelee.pok.png ├── Screenshot_2019-05-02-18-04-50-334_cn.bavelee.pok.png ├── app ├── .gitignore ├── build.gradle ├── libs │ └── ColorPickerPreference │ │ ├── .classpath │ │ ├── .gitignore │ │ ├── .idea │ │ ├── codeStyles │ │ │ └── Project.xml │ │ ├── gradle.xml │ │ ├── misc.xml │ │ ├── modules.xml │ │ ├── runConfigurations.xml │ │ └── workspace.xml │ │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ │ ├── build.gradle │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── local.properties │ │ ├── proguard-rules.pro │ │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── net │ │ │ └── margaritov │ │ │ └── colorpickerpreference │ │ │ └── ApplicationTest.java │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── net │ │ │ └── margaritov │ │ │ └── preference │ │ │ └── colorpicker │ │ │ ├── AlphaPatternDrawable.java │ │ │ ├── ColorPickerDialog.java │ │ │ ├── ColorPickerPanelView.java │ │ │ ├── ColorPickerPreference.java │ │ │ └── ColorPickerView.java │ │ └── res │ │ ├── layout-land │ │ └── dialog_color_picker.xml │ │ ├── layout │ │ └── dialog_color_picker.xml │ │ └── values │ │ └── strings.xml ├── proguard-rules.pro ├── release │ ├── app-release.apk │ └── output.json └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── bavelee │ │ └── pokeinstaller │ │ ├── BackgroundInstallActivity.java │ │ ├── PokeInstallerActivity.java │ │ ├── Prefs.java │ │ ├── SettingsActivity.java │ │ ├── ShellUtils.java │ │ └── apk │ │ ├── APKCommander.java │ │ ├── ApkInfo.java │ │ ├── ContentUriUtils.java │ │ └── ICommanderCallback.java │ └── res │ ├── drawable │ ├── bg_hover.xml │ └── ic_settings.xml │ ├── layout │ ├── act_poke_installer.xml │ ├── info_item.xml │ └── info_item_permission.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-zh-rTW │ └── strings.xml │ ├── values-zh │ └── strings.xml │ ├── values │ ├── arrays.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── prefs.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── module_donatedialog ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── bavelee │ │ └── donatedialog │ │ └── DonateToMe.java │ └── res │ ├── drawable │ ├── alipay_money_revised.png │ └── wechat_money_revised.png │ ├── layout │ └── layout_donate_dialog.xml │ ├── values-zh │ └── strings.xml │ └── values │ └── strings.xml ├── pokeinstaller.keystore └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PokeInstaller 2 | An android package installer alternatives under root 3 | -------------------------------------------------------------------------------- /Screenshot_2019-05-02-18-04-40-864_cn.bavelee.pok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/Screenshot_2019-05-02-18-04-40-864_cn.bavelee.pok.png -------------------------------------------------------------------------------- /Screenshot_2019-05-02-18-04-50-334_cn.bavelee.pok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/Screenshot_2019-05-02-18-04-50-334_cn.bavelee.pok.png -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "cn.bavelee.pokeinstaller" 7 | minSdkVersion 21 8 | targetSdkVersion 28 9 | versionCode 20 10 | versionName "2.9" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | } 19 | 20 | dependencies { 21 | implementation fileTree(include: ['*.jar'], dir: 'libs') 22 | implementation project(':ColorPickerPreference') 23 | implementation project(':module_donatedialog') 24 | implementation 'com.github.florent37:runtime-permission:1.0.1' 25 | implementation 'com.android.support:support-fragment:28.0.0' 26 | } 27 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 1556454767703 74 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 105 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=.. 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | bintrayRepo = 'android-ColorPickerPreference' 5 | bintrayName = 'android-colorpickerpreference' 6 | 7 | publishedGroupId = 'net.margaritov.preference.colorpicker.ColorPickerPreference' 8 | libraryName = 'ColorPickerPreference' 9 | artifact = 'ColorPickerPreference' 10 | 11 | libraryDescription = 'ColorPickerPreference for android to create color picker in preferences. Project created as Library' 12 | 13 | siteUrl = 'https://github.com/attenzione/android-ColorPickerPreference' 14 | gitUrl = 'https://github.com/attenzione/android-ColorPickerPreference.git' 15 | 16 | libraryVersion = '1.0.0' 17 | 18 | developerId = 'attenzione' 19 | developerName = 'Sergey Margaritov' 20 | developerEmail = 'github@semar.in' 21 | 22 | licenseName = 'The Apache Software License, Version 2.0' 23 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 24 | allLicenses = ["Apache-2.0"] 25 | } 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | defaultConfig { 31 | minSdkVersion 7 32 | targetSdkVersion 28 33 | versionCode 1 34 | versionName "1.0" 35 | } 36 | buildTypes { 37 | release { 38 | minifyEnabled false 39 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 40 | } 41 | } 42 | } 43 | 44 | dependencies { 45 | implementation fileTree(include: ['*.jar'], dir: 'libs') 46 | } 47 | 48 | // Place it at the end of the file 49 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 50 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' 51 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/app/libs/ColorPickerPreference/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Apr 28 20:32:56 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sun Apr 28 20:32:47 CST 2019 8 | sdk.dir=/Users/imbavelee/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -optimizationpasses 5 2 | -dontusemixedcaseclassnames 3 | -dontskipnonpubliclibraryclasses 4 | -dontpreverify 5 | -verbose 6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 7 | 8 | -keep public class * extends android.app.Activity 9 | -keep public class * extends android.app.Application 10 | -keep public class * extends android.app.Service 11 | -keep public class * extends android.content.BroadcastReceiver 12 | -keep public class * extends android.content.ContentProvider 13 | -keep public class com.android.vending.licensing.ILicensingService 14 | 15 | -keepclasseswithmembernames class * { 16 | native ; 17 | } 18 | 19 | -keepclasseswithmembernames class * { 20 | public (android.content.Context, android.util.AttributeSet); 21 | } 22 | 23 | -keepclasseswithmembernames class * { 24 | public (android.content.Context, android.util.AttributeSet, int); 25 | } 26 | 27 | -keepclassmembers enum * { 28 | public static **[] values(); 29 | public static ** valueOf(java.lang.String); 30 | } 31 | 32 | -keep class * implements android.os.Parcelable { 33 | public static final android.os.Parcelable$Creator *; 34 | } 35 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/androidTest/java/net/margaritov/colorpickerpreference/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package net.margaritov.colorpickerpreference; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/java/net/margaritov/preference/colorpicker/AlphaPatternDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Daniel Nilsson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.margaritov.preference.colorpicker; 18 | 19 | import android.graphics.Bitmap; 20 | import android.graphics.Bitmap.Config; 21 | import android.graphics.Canvas; 22 | import android.graphics.ColorFilter; 23 | import android.graphics.Paint; 24 | import android.graphics.Rect; 25 | import android.graphics.drawable.Drawable; 26 | 27 | /** 28 | * This drawable that draws a simple white and gray chessboard pattern. 29 | * It's pattern you will often see as a background behind a 30 | * partly transparent image in many applications. 31 | * 32 | * @author Daniel Nilsson 33 | */ 34 | public class AlphaPatternDrawable extends Drawable { 35 | 36 | private int mRectangleSize = 10; 37 | 38 | private Paint mPaint = new Paint(); 39 | private Paint mPaintWhite = new Paint(); 40 | private Paint mPaintGray = new Paint(); 41 | 42 | private int numRectanglesHorizontal; 43 | private int numRectanglesVertical; 44 | 45 | /** 46 | * Bitmap in which the pattern will be cahched. 47 | */ 48 | private Bitmap mBitmap; 49 | 50 | public AlphaPatternDrawable(int rectangleSize) { 51 | mRectangleSize = rectangleSize; 52 | mPaintWhite.setColor(0xffffffff); 53 | mPaintGray.setColor(0xffcbcbcb); 54 | } 55 | 56 | @Override 57 | public void draw(Canvas canvas) { 58 | canvas.drawBitmap(mBitmap, null, getBounds(), mPaint); 59 | } 60 | 61 | @Override 62 | public int getOpacity() { 63 | return 0; 64 | } 65 | 66 | @Override 67 | public void setAlpha(int alpha) { 68 | throw new UnsupportedOperationException("Alpha is not supported by this drawwable."); 69 | } 70 | 71 | @Override 72 | public void setColorFilter(ColorFilter cf) { 73 | throw new UnsupportedOperationException("ColorFilter is not supported by this drawwable."); 74 | } 75 | 76 | @Override 77 | protected void onBoundsChange(Rect bounds) { 78 | super.onBoundsChange(bounds); 79 | 80 | int height = bounds.height(); 81 | int width = bounds.width(); 82 | 83 | numRectanglesHorizontal = (int) Math.ceil((width / mRectangleSize)); 84 | numRectanglesVertical = (int) Math.ceil(height / mRectangleSize); 85 | 86 | generatePatternBitmap(); 87 | 88 | } 89 | 90 | /** 91 | * This will generate a bitmap with the pattern 92 | * as big as the rectangle we were allow to draw on. 93 | * We do this to chache the bitmap so we don't need to 94 | * recreate it each time draw() is called since it 95 | * takes a few milliseconds. 96 | */ 97 | private void generatePatternBitmap() { 98 | 99 | if (getBounds().width() <= 0 || getBounds().height() <= 0) { 100 | return; 101 | } 102 | 103 | mBitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Config.ARGB_8888); 104 | Canvas canvas = new Canvas(mBitmap); 105 | 106 | Rect r = new Rect(); 107 | boolean verticalStartWhite = true; 108 | for (int i = 0; i <= numRectanglesVertical; i++) { 109 | 110 | boolean isWhite = verticalStartWhite; 111 | for (int j = 0; j <= numRectanglesHorizontal; j++) { 112 | 113 | r.top = i * mRectangleSize; 114 | r.left = j * mRectangleSize; 115 | r.bottom = r.top + mRectangleSize; 116 | r.right = r.left + mRectangleSize; 117 | 118 | canvas.drawRect(r, isWhite ? mPaintWhite : mPaintGray); 119 | 120 | isWhite = !isWhite; 121 | } 122 | 123 | verticalStartWhite = !verticalStartWhite; 124 | 125 | } 126 | 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/java/net/margaritov/preference/colorpicker/ColorPickerDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Daniel Nilsson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.margaritov.preference.colorpicker; 18 | 19 | import android.app.Dialog; 20 | import android.content.Context; 21 | import android.content.res.ColorStateList; 22 | import android.graphics.Color; 23 | import android.graphics.PixelFormat; 24 | import android.os.Bundle; 25 | import android.text.InputFilter; 26 | import android.text.InputType; 27 | import android.view.KeyEvent; 28 | import android.view.LayoutInflater; 29 | import android.view.View; 30 | import android.view.ViewTreeObserver; 31 | import android.view.inputmethod.EditorInfo; 32 | import android.view.inputmethod.InputMethodManager; 33 | import android.widget.EditText; 34 | import android.widget.LinearLayout; 35 | import android.widget.TextView; 36 | 37 | import java.util.Locale; 38 | 39 | public class ColorPickerDialog 40 | extends 41 | Dialog 42 | implements 43 | ColorPickerView.OnColorChangedListener, 44 | View.OnClickListener, ViewTreeObserver.OnGlobalLayoutListener { 45 | 46 | private ColorPickerView mColorPicker; 47 | 48 | private ColorPickerPanelView mOldColor; 49 | private ColorPickerPanelView mNewColor; 50 | 51 | private EditText mHexVal; 52 | private boolean mHexValueEnabled = false; 53 | private ColorStateList mHexDefaultTextColor; 54 | 55 | private OnColorChangedListener mListener; 56 | private int mOrientation; 57 | private View mLayout; 58 | 59 | @Override 60 | public void onGlobalLayout() { 61 | if (getContext().getResources().getConfiguration().orientation != mOrientation) { 62 | final int oldcolor = mOldColor.getColor(); 63 | final int newcolor = mNewColor.getColor(); 64 | mLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); 65 | setUp(oldcolor); 66 | mNewColor.setColor(newcolor); 67 | mColorPicker.setColor(newcolor); 68 | } 69 | } 70 | 71 | public interface OnColorChangedListener { 72 | public void onColorChanged(int color); 73 | } 74 | 75 | public ColorPickerDialog(Context context, int initialColor) { 76 | super(context); 77 | 78 | init(initialColor); 79 | } 80 | 81 | private void init(int color) { 82 | // To fight color banding. 83 | getWindow().setFormat(PixelFormat.RGBA_8888); 84 | 85 | setUp(color); 86 | 87 | } 88 | 89 | private void setUp(int color) { 90 | 91 | LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 92 | 93 | mLayout = inflater.inflate(R.layout.dialog_color_picker, null); 94 | mLayout.getViewTreeObserver().addOnGlobalLayoutListener(this); 95 | 96 | mOrientation = getContext().getResources().getConfiguration().orientation; 97 | setContentView(mLayout); 98 | 99 | setTitle(R.string.dialog_color_picker); 100 | 101 | mColorPicker = (ColorPickerView) mLayout.findViewById(R.id.color_picker_view); 102 | mOldColor = (ColorPickerPanelView) mLayout.findViewById(R.id.old_color_panel); 103 | mNewColor = (ColorPickerPanelView) mLayout.findViewById(R.id.new_color_panel); 104 | 105 | mHexVal = (EditText) mLayout.findViewById(R.id.hex_val); 106 | mHexVal.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); 107 | mHexDefaultTextColor = mHexVal.getTextColors(); 108 | 109 | mHexVal.setOnEditorActionListener(new TextView.OnEditorActionListener() { 110 | 111 | @Override 112 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 113 | if (actionId == EditorInfo.IME_ACTION_DONE) { 114 | InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 115 | imm.hideSoftInputFromWindow(v.getWindowToken(), 0); 116 | String s = mHexVal.getText().toString(); 117 | if (s.length() > 5 || s.length() < 10) { 118 | try { 119 | int c = ColorPickerPreference.convertToColorInt(s.toString()); 120 | mColorPicker.setColor(c, true); 121 | mHexVal.setTextColor(mHexDefaultTextColor); 122 | } catch (IllegalArgumentException e) { 123 | mHexVal.setTextColor(Color.RED); 124 | } 125 | } else { 126 | mHexVal.setTextColor(Color.RED); 127 | } 128 | return true; 129 | } 130 | return false; 131 | } 132 | }); 133 | 134 | ((LinearLayout) mOldColor.getParent()).setPadding( 135 | Math.round(mColorPicker.getDrawingOffset()), 136 | 0, 137 | Math.round(mColorPicker.getDrawingOffset()), 138 | 0 139 | ); 140 | 141 | mOldColor.setOnClickListener(this); 142 | mNewColor.setOnClickListener(this); 143 | mColorPicker.setOnColorChangedListener(this); 144 | mOldColor.setColor(color); 145 | mColorPicker.setColor(color, true); 146 | 147 | } 148 | 149 | @Override 150 | public void onColorChanged(int color) { 151 | 152 | mNewColor.setColor(color); 153 | 154 | if (mHexValueEnabled) 155 | updateHexValue(color); 156 | 157 | /* 158 | if (mListener != null) { 159 | mListener.onColorChanged(color); 160 | } 161 | */ 162 | 163 | } 164 | 165 | public void setHexValueEnabled(boolean enable) { 166 | mHexValueEnabled = enable; 167 | if (enable) { 168 | mHexVal.setVisibility(View.VISIBLE); 169 | updateHexLengthFilter(); 170 | updateHexValue(getColor()); 171 | } else 172 | mHexVal.setVisibility(View.GONE); 173 | } 174 | 175 | public boolean getHexValueEnabled() { 176 | return mHexValueEnabled; 177 | } 178 | 179 | private void updateHexLengthFilter() { 180 | if (getAlphaSliderVisible()) 181 | mHexVal.setFilters(new InputFilter[]{new InputFilter.LengthFilter(9)}); 182 | else 183 | mHexVal.setFilters(new InputFilter[]{new InputFilter.LengthFilter(7)}); 184 | } 185 | 186 | private void updateHexValue(int color) { 187 | if (getAlphaSliderVisible()) { 188 | mHexVal.setText(ColorPickerPreference.convertToARGB(color).toUpperCase(Locale.getDefault())); 189 | } else { 190 | mHexVal.setText(ColorPickerPreference.convertToRGB(color).toUpperCase(Locale.getDefault())); 191 | } 192 | mHexVal.setTextColor(mHexDefaultTextColor); 193 | } 194 | 195 | public void setAlphaSliderVisible(boolean visible) { 196 | mColorPicker.setAlphaSliderVisible(visible); 197 | if (mHexValueEnabled) { 198 | updateHexLengthFilter(); 199 | updateHexValue(getColor()); 200 | } 201 | } 202 | 203 | public boolean getAlphaSliderVisible() { 204 | return mColorPicker.getAlphaSliderVisible(); 205 | } 206 | 207 | /** 208 | * Set a OnColorChangedListener to get notified when the color 209 | * selected by the user has changed. 210 | * 211 | * @param listener 212 | */ 213 | public void setOnColorChangedListener(OnColorChangedListener listener) { 214 | mListener = listener; 215 | } 216 | 217 | public int getColor() { 218 | return mColorPicker.getColor(); 219 | } 220 | 221 | @Override 222 | public void onClick(View v) { 223 | if (v.getId() == R.id.new_color_panel) { 224 | if (mListener != null) { 225 | mListener.onColorChanged(mNewColor.getColor()); 226 | } 227 | } 228 | dismiss(); 229 | } 230 | 231 | @Override 232 | public Bundle onSaveInstanceState() { 233 | Bundle state = super.onSaveInstanceState(); 234 | state.putInt("old_color", mOldColor.getColor()); 235 | state.putInt("new_color", mNewColor.getColor()); 236 | return state; 237 | } 238 | 239 | @Override 240 | public void onRestoreInstanceState(Bundle savedInstanceState) { 241 | super.onRestoreInstanceState(savedInstanceState); 242 | mOldColor.setColor(savedInstanceState.getInt("old_color")); 243 | mColorPicker.setColor(savedInstanceState.getInt("new_color"), true); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/java/net/margaritov/preference/colorpicker/ColorPickerPanelView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Daniel Nilsson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.margaritov.preference.colorpicker; 18 | 19 | import android.content.Context; 20 | import android.graphics.Canvas; 21 | import android.graphics.Paint; 22 | import android.graphics.RectF; 23 | import android.util.AttributeSet; 24 | import android.view.View; 25 | 26 | /** 27 | * This class draws a panel which which will be filled with a color which can be set. 28 | * It can be used to show the currently selected color which you will get from 29 | * the {@link ColorPickerView}. 30 | * 31 | * @author Daniel Nilsson 32 | */ 33 | public class ColorPickerPanelView extends View { 34 | 35 | /** 36 | * The width in pixels of the border 37 | * surrounding the color panel. 38 | */ 39 | private final static float BORDER_WIDTH_PX = 1; 40 | 41 | private float mDensity = 1f; 42 | 43 | private int mBorderColor = 0xff6E6E6E; 44 | private int mColor = 0xff000000; 45 | 46 | private Paint mBorderPaint; 47 | private Paint mColorPaint; 48 | 49 | private RectF mDrawingRect; 50 | private RectF mColorRect; 51 | 52 | private AlphaPatternDrawable mAlphaPattern; 53 | 54 | 55 | public ColorPickerPanelView(Context context) { 56 | this(context, null); 57 | } 58 | 59 | public ColorPickerPanelView(Context context, AttributeSet attrs) { 60 | this(context, attrs, 0); 61 | } 62 | 63 | public ColorPickerPanelView(Context context, AttributeSet attrs, int defStyle) { 64 | super(context, attrs, defStyle); 65 | init(); 66 | } 67 | 68 | private void init() { 69 | mBorderPaint = new Paint(); 70 | mColorPaint = new Paint(); 71 | mDensity = getContext().getResources().getDisplayMetrics().density; 72 | } 73 | 74 | 75 | @Override 76 | protected void onDraw(Canvas canvas) { 77 | 78 | final RectF rect = mColorRect; 79 | 80 | if (BORDER_WIDTH_PX > 0) { 81 | mBorderPaint.setColor(mBorderColor); 82 | canvas.drawRect(mDrawingRect, mBorderPaint); 83 | } 84 | 85 | if (mAlphaPattern != null) { 86 | mAlphaPattern.draw(canvas); 87 | } 88 | 89 | mColorPaint.setColor(mColor); 90 | 91 | canvas.drawRect(rect, mColorPaint); 92 | } 93 | 94 | @Override 95 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 96 | 97 | int width = MeasureSpec.getSize(widthMeasureSpec); 98 | int height = MeasureSpec.getSize(heightMeasureSpec); 99 | 100 | setMeasuredDimension(width, height); 101 | } 102 | 103 | @Override 104 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 105 | super.onSizeChanged(w, h, oldw, oldh); 106 | 107 | mDrawingRect = new RectF(); 108 | mDrawingRect.left = getPaddingLeft(); 109 | mDrawingRect.right = w - getPaddingRight(); 110 | mDrawingRect.top = getPaddingTop(); 111 | mDrawingRect.bottom = h - getPaddingBottom(); 112 | 113 | setUpColorRect(); 114 | 115 | } 116 | 117 | private void setUpColorRect() { 118 | final RectF dRect = mDrawingRect; 119 | 120 | float left = dRect.left + BORDER_WIDTH_PX; 121 | float top = dRect.top + BORDER_WIDTH_PX; 122 | float bottom = dRect.bottom - BORDER_WIDTH_PX; 123 | float right = dRect.right - BORDER_WIDTH_PX; 124 | 125 | mColorRect = new RectF(left, top, right, bottom); 126 | 127 | mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity)); 128 | 129 | mAlphaPattern.setBounds( 130 | Math.round(mColorRect.left), 131 | Math.round(mColorRect.top), 132 | Math.round(mColorRect.right), 133 | Math.round(mColorRect.bottom) 134 | ); 135 | 136 | } 137 | 138 | /** 139 | * Set the color that should be shown by this view. 140 | * 141 | * @param color 142 | */ 143 | public void setColor(int color) { 144 | mColor = color; 145 | invalidate(); 146 | } 147 | 148 | /** 149 | * Get the color currently show by this view. 150 | * 151 | * @return 152 | */ 153 | public int getColor() { 154 | return mColor; 155 | } 156 | 157 | /** 158 | * Set the color of the border surrounding the panel. 159 | * 160 | * @param color 161 | */ 162 | public void setBorderColor(int color) { 163 | mBorderColor = color; 164 | invalidate(); 165 | } 166 | 167 | /** 168 | * Get the color of the border surrounding the panel. 169 | */ 170 | public int getBorderColor() { 171 | return mBorderColor; 172 | } 173 | 174 | } -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/java/net/margaritov/preference/colorpicker/ColorPickerPreference.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Sergey Margaritov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.margaritov.preference.colorpicker; 18 | 19 | import android.content.Context; 20 | import android.content.res.TypedArray; 21 | import android.graphics.Bitmap; 22 | import android.graphics.Bitmap.Config; 23 | import android.graphics.Color; 24 | import android.os.Bundle; 25 | import android.os.Parcel; 26 | import android.os.Parcelable; 27 | import android.preference.Preference; 28 | import android.util.AttributeSet; 29 | import android.view.View; 30 | import android.widget.ImageView; 31 | import android.widget.LinearLayout; 32 | 33 | /** 34 | * A preference type that allows a user to choose a time 35 | * 36 | * @author Sergey Margaritov 37 | */ 38 | public class ColorPickerPreference 39 | extends 40 | Preference 41 | implements 42 | Preference.OnPreferenceClickListener, 43 | ColorPickerDialog.OnColorChangedListener { 44 | 45 | View mView; 46 | ColorPickerDialog mDialog; 47 | private int mValue = Color.BLACK; 48 | private float mDensity = 0; 49 | private boolean mAlphaSliderEnabled = false; 50 | private boolean mHexValueEnabled = false; 51 | 52 | public ColorPickerPreference(Context context) { 53 | super(context); 54 | init(context, null); 55 | } 56 | 57 | public ColorPickerPreference(Context context, AttributeSet attrs) { 58 | super(context, attrs); 59 | init(context, attrs); 60 | } 61 | 62 | public ColorPickerPreference(Context context, AttributeSet attrs, int defStyle) { 63 | super(context, attrs, defStyle); 64 | init(context, attrs); 65 | } 66 | 67 | /**Method edited by 68 | * @author Anna Berkovitch 69 | * added functionality to accept hex string as defaultValue 70 | * and to properly persist resources reference string, such as @color/someColor 71 | * previously persisted 0*/ 72 | @Override 73 | protected Object onGetDefaultValue(TypedArray a, int index) { 74 | int colorInt; 75 | String mHexDefaultValue = a.getString(index); 76 | if (mHexDefaultValue != null && mHexDefaultValue.startsWith("#")) { 77 | colorInt = convertToColorInt(mHexDefaultValue); 78 | return colorInt; 79 | 80 | } else { 81 | return a.getColor(index, Color.BLACK); 82 | } 83 | } 84 | 85 | @Override 86 | protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 87 | onColorChanged(restoreValue ? getPersistedInt(mValue) : (Integer) defaultValue); 88 | } 89 | 90 | private void init(Context context, AttributeSet attrs) { 91 | mDensity = getContext().getResources().getDisplayMetrics().density; 92 | setOnPreferenceClickListener(this); 93 | if (attrs != null) { 94 | mAlphaSliderEnabled = attrs.getAttributeBooleanValue(null, "alphaSlider", false); 95 | mHexValueEnabled = attrs.getAttributeBooleanValue(null, "hexValue", false); 96 | } 97 | } 98 | 99 | @Override 100 | protected void onBindView(View view) { 101 | super.onBindView(view); 102 | mView = view; 103 | setPreviewColor(); 104 | } 105 | 106 | private void setPreviewColor() { 107 | if (mView == null) return; 108 | ImageView iView = new ImageView(getContext()); 109 | LinearLayout widgetFrameView = ((LinearLayout) mView.findViewById(android.R.id.widget_frame)); 110 | if (widgetFrameView == null) return; 111 | widgetFrameView.setVisibility(View.VISIBLE); 112 | widgetFrameView.setPadding( 113 | widgetFrameView.getPaddingLeft(), 114 | widgetFrameView.getPaddingTop(), 115 | (int) (mDensity * 8), 116 | widgetFrameView.getPaddingBottom() 117 | ); 118 | // remove already create preview image 119 | int count = widgetFrameView.getChildCount(); 120 | if (count > 0) { 121 | widgetFrameView.removeViews(0, count); 122 | } 123 | widgetFrameView.addView(iView); 124 | widgetFrameView.setMinimumWidth(0); 125 | iView.setBackgroundDrawable(new AlphaPatternDrawable((int) (5 * mDensity))); 126 | iView.setImageBitmap(getPreviewBitmap()); 127 | } 128 | 129 | private Bitmap getPreviewBitmap() { 130 | int d = (int) (mDensity * 31); //30dip 131 | int color = mValue; 132 | Bitmap bm = Bitmap.createBitmap(d, d, Config.ARGB_8888); 133 | int w = bm.getWidth(); 134 | int h = bm.getHeight(); 135 | int c = color; 136 | for (int i = 0; i < w; i++) { 137 | for (int j = i; j < h; j++) { 138 | c = (i <= 1 || j <= 1 || i >= w - 2 || j >= h - 2) ? Color.GRAY : color; 139 | bm.setPixel(i, j, c); 140 | if (i != j) { 141 | bm.setPixel(j, i, c); 142 | } 143 | } 144 | } 145 | 146 | return bm; 147 | } 148 | 149 | @Override 150 | public void onColorChanged(int color) { 151 | if (isPersistent()) { 152 | persistInt(color); 153 | } 154 | mValue = color; 155 | setPreviewColor(); 156 | try { 157 | getOnPreferenceChangeListener().onPreferenceChange(this, color); 158 | } catch (NullPointerException e) { 159 | 160 | } 161 | } 162 | 163 | public boolean onPreferenceClick(Preference preference) { 164 | showDialog(null); 165 | return false; 166 | } 167 | 168 | protected void showDialog(Bundle state) { 169 | mDialog = new ColorPickerDialog(getContext(), mValue); 170 | mDialog.setOnColorChangedListener(this); 171 | if (mAlphaSliderEnabled) { 172 | mDialog.setAlphaSliderVisible(true); 173 | } 174 | if (mHexValueEnabled) { 175 | mDialog.setHexValueEnabled(true); 176 | } 177 | if (state != null) { 178 | mDialog.onRestoreInstanceState(state); 179 | } 180 | mDialog.show(); 181 | } 182 | 183 | /** 184 | * Toggle Alpha Slider visibility (by default it's disabled) 185 | * 186 | * @param enable 187 | */ 188 | public void setAlphaSliderEnabled(boolean enable) { 189 | mAlphaSliderEnabled = enable; 190 | } 191 | 192 | /** 193 | * Toggle Hex Value visibility (by default it's disabled) 194 | * 195 | * @param enable 196 | */ 197 | public void setHexValueEnabled(boolean enable) { 198 | mHexValueEnabled = enable; 199 | } 200 | 201 | /** 202 | * For custom purposes. Not used by ColorPickerPreferrence 203 | * 204 | * @param color 205 | * @author Unknown 206 | */ 207 | public static String convertToARGB(int color) { 208 | String alpha = Integer.toHexString(Color.alpha(color)); 209 | String red = Integer.toHexString(Color.red(color)); 210 | String green = Integer.toHexString(Color.green(color)); 211 | String blue = Integer.toHexString(Color.blue(color)); 212 | 213 | if (alpha.length() == 1) { 214 | alpha = "0" + alpha; 215 | } 216 | 217 | if (red.length() == 1) { 218 | red = "0" + red; 219 | } 220 | 221 | if (green.length() == 1) { 222 | green = "0" + green; 223 | } 224 | 225 | if (blue.length() == 1) { 226 | blue = "0" + blue; 227 | } 228 | 229 | return "#" + alpha + red + green + blue; 230 | } 231 | 232 | /** 233 | * Method currently used by onGetDefaultValue method to 234 | * convert hex string provided in android:defaultValue to color integer. 235 | * 236 | * @param color 237 | * @return A string representing the hex value of color, 238 | * without the alpha value 239 | * @author Charles Rosaaen 240 | */ 241 | public static String convertToRGB(int color) { 242 | String red = Integer.toHexString(Color.red(color)); 243 | String green = Integer.toHexString(Color.green(color)); 244 | String blue = Integer.toHexString(Color.blue(color)); 245 | 246 | if (red.length() == 1) { 247 | red = "0" + red; 248 | } 249 | 250 | if (green.length() == 1) { 251 | green = "0" + green; 252 | } 253 | 254 | if (blue.length() == 1) { 255 | blue = "0" + blue; 256 | } 257 | 258 | return "#" + red + green + blue; 259 | } 260 | 261 | /** 262 | * For custom purposes. Not used by ColorPickerPreferrence 263 | * 264 | * @param argb 265 | * @throws NumberFormatException 266 | * @author Unknown 267 | */ 268 | public static int convertToColorInt(String argb) throws IllegalArgumentException { 269 | 270 | if (!argb.startsWith("#")) { 271 | argb = "#" + argb; 272 | } 273 | 274 | return Color.parseColor(argb); 275 | } 276 | 277 | @Override 278 | protected Parcelable onSaveInstanceState() { 279 | final Parcelable superState = super.onSaveInstanceState(); 280 | if (mDialog == null || !mDialog.isShowing()) { 281 | return superState; 282 | } 283 | 284 | final SavedState myState = new SavedState(superState); 285 | myState.dialogBundle = mDialog.onSaveInstanceState(); 286 | return myState; 287 | } 288 | 289 | @Override 290 | protected void onRestoreInstanceState(Parcelable state) { 291 | if (state == null || !(state instanceof SavedState)) { 292 | // Didn't save state for us in onSaveInstanceState 293 | super.onRestoreInstanceState(state); 294 | return; 295 | } 296 | 297 | SavedState myState = (SavedState) state; 298 | super.onRestoreInstanceState(myState.getSuperState()); 299 | showDialog(myState.dialogBundle); 300 | } 301 | 302 | private static class SavedState extends BaseSavedState { 303 | Bundle dialogBundle; 304 | 305 | public SavedState(Parcel source) { 306 | super(source); 307 | dialogBundle = source.readBundle(); 308 | } 309 | 310 | @Override 311 | public void writeToParcel(Parcel dest, int flags) { 312 | super.writeToParcel(dest, flags); 313 | dest.writeBundle(dialogBundle); 314 | } 315 | 316 | public SavedState(Parcelable superState) { 317 | super(superState); 318 | } 319 | 320 | @SuppressWarnings("unused") 321 | public static final Parcelable.Creator CREATOR = 322 | new Parcelable.Creator() { 323 | public SavedState createFromParcel(Parcel in) { 324 | return new SavedState(in); 325 | } 326 | 327 | public SavedState[] newArray(int size) { 328 | return new SavedState[size]; 329 | } 330 | }; 331 | } 332 | } -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/java/net/margaritov/preference/colorpicker/ColorPickerView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Daniel Nilsson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.margaritov.preference.colorpicker; 18 | 19 | import android.content.Context; 20 | import android.graphics.Canvas; 21 | import android.graphics.Color; 22 | import android.graphics.ComposeShader; 23 | import android.graphics.LinearGradient; 24 | import android.graphics.Paint; 25 | import android.graphics.Paint.Align; 26 | import android.graphics.Paint.Style; 27 | import android.graphics.Point; 28 | import android.graphics.PorterDuff; 29 | import android.graphics.RectF; 30 | import android.graphics.Shader; 31 | import android.graphics.Shader.TileMode; 32 | import android.util.AttributeSet; 33 | import android.view.MotionEvent; 34 | import android.view.View; 35 | 36 | /** 37 | * Displays a color picker to the user and allow them 38 | * to select a color. A slider for the alpha channel is 39 | * also available. Enable it by setting 40 | * setAlphaSliderVisible(boolean) to true. 41 | * 42 | * @author Daniel Nilsson 43 | */ 44 | public class ColorPickerView extends View { 45 | 46 | private final static int PANEL_SAT_VAL = 0; 47 | private final static int PANEL_HUE = 1; 48 | private final static int PANEL_ALPHA = 2; 49 | 50 | /** 51 | * The width in pixels of the border 52 | * surrounding all color panels. 53 | */ 54 | private final static float BORDER_WIDTH_PX = 1; 55 | 56 | /** 57 | * The width in dp of the hue panel. 58 | */ 59 | private float HUE_PANEL_WIDTH = 30f; 60 | /** 61 | * The height in dp of the alpha panel 62 | */ 63 | private float ALPHA_PANEL_HEIGHT = 20f; 64 | /** 65 | * The distance in dp between the different 66 | * color panels. 67 | */ 68 | private float PANEL_SPACING = 10f; 69 | /** 70 | * The radius in dp of the color palette tracker circle. 71 | */ 72 | private float PALETTE_CIRCLE_TRACKER_RADIUS = 5f; 73 | /** 74 | * The dp which the tracker of the hue or alpha panel 75 | * will extend outside of its bounds. 76 | */ 77 | private float RECTANGLE_TRACKER_OFFSET = 2f; 78 | 79 | 80 | private float mDensity = 1f; 81 | 82 | private OnColorChangedListener mListener; 83 | 84 | private Paint mSatValPaint; 85 | private Paint mSatValTrackerPaint; 86 | 87 | private Paint mHuePaint; 88 | private Paint mHueTrackerPaint; 89 | 90 | private Paint mAlphaPaint; 91 | private Paint mAlphaTextPaint; 92 | 93 | private Paint mBorderPaint; 94 | 95 | private Shader mValShader; 96 | private Shader mSatShader; 97 | private Shader mHueShader; 98 | private Shader mAlphaShader; 99 | 100 | private int mAlpha = 0xff; 101 | private float mHue = 360f; 102 | private float mSat = 0f; 103 | private float mVal = 0f; 104 | 105 | private String mAlphaSliderText = ""; 106 | private int mSliderTrackerColor = 0xff1c1c1c; 107 | private int mBorderColor = 0xff6E6E6E; 108 | private boolean mShowAlphaPanel = false; 109 | 110 | /* 111 | * To remember which panel that has the "focus" when 112 | * processing hardware button data. 113 | */ 114 | private int mLastTouchedPanel = PANEL_SAT_VAL; 115 | 116 | /** 117 | * Offset from the edge we must have or else 118 | * the finger tracker will get clipped when 119 | * it is drawn outside of the view. 120 | */ 121 | private float mDrawingOffset; 122 | 123 | 124 | /* 125 | * Distance form the edges of the view 126 | * of where we are allowed to draw. 127 | */ 128 | private RectF mDrawingRect; 129 | 130 | private RectF mSatValRect; 131 | private RectF mHueRect; 132 | private RectF mAlphaRect; 133 | 134 | private AlphaPatternDrawable mAlphaPattern; 135 | 136 | private Point mStartTouchPoint = null; 137 | 138 | public interface OnColorChangedListener { 139 | public void onColorChanged(int color); 140 | } 141 | 142 | public ColorPickerView(Context context) { 143 | this(context, null); 144 | } 145 | 146 | public ColorPickerView(Context context, AttributeSet attrs) { 147 | this(context, attrs, 0); 148 | } 149 | 150 | public ColorPickerView(Context context, AttributeSet attrs, int defStyle) { 151 | super(context, attrs, defStyle); 152 | init(); 153 | } 154 | 155 | private void init() { 156 | mDensity = getContext().getResources().getDisplayMetrics().density; 157 | PALETTE_CIRCLE_TRACKER_RADIUS *= mDensity; 158 | RECTANGLE_TRACKER_OFFSET *= mDensity; 159 | HUE_PANEL_WIDTH *= mDensity; 160 | ALPHA_PANEL_HEIGHT *= mDensity; 161 | PANEL_SPACING = PANEL_SPACING * mDensity; 162 | 163 | mDrawingOffset = calculateRequiredOffset(); 164 | 165 | initPaintTools(); 166 | 167 | //Needed for receiving trackball motion events. 168 | setFocusable(true); 169 | setFocusableInTouchMode(true); 170 | } 171 | 172 | private void initPaintTools() { 173 | 174 | mSatValPaint = new Paint(); 175 | mSatValTrackerPaint = new Paint(); 176 | mHuePaint = new Paint(); 177 | mHueTrackerPaint = new Paint(); 178 | mAlphaPaint = new Paint(); 179 | mAlphaTextPaint = new Paint(); 180 | mBorderPaint = new Paint(); 181 | 182 | 183 | mSatValTrackerPaint.setStyle(Style.STROKE); 184 | mSatValTrackerPaint.setStrokeWidth(2f * mDensity); 185 | mSatValTrackerPaint.setAntiAlias(true); 186 | 187 | mHueTrackerPaint.setColor(mSliderTrackerColor); 188 | mHueTrackerPaint.setStyle(Style.STROKE); 189 | mHueTrackerPaint.setStrokeWidth(2f * mDensity); 190 | mHueTrackerPaint.setAntiAlias(true); 191 | 192 | mAlphaTextPaint.setColor(0xff1c1c1c); 193 | mAlphaTextPaint.setTextSize(14f * mDensity); 194 | mAlphaTextPaint.setAntiAlias(true); 195 | mAlphaTextPaint.setTextAlign(Align.CENTER); 196 | mAlphaTextPaint.setFakeBoldText(true); 197 | 198 | 199 | } 200 | 201 | private float calculateRequiredOffset() { 202 | float offset = Math.max(PALETTE_CIRCLE_TRACKER_RADIUS, RECTANGLE_TRACKER_OFFSET); 203 | offset = Math.max(offset, BORDER_WIDTH_PX * mDensity); 204 | 205 | return offset * 1.5f; 206 | } 207 | 208 | private int[] buildHueColorArray() { 209 | 210 | int[] hue = new int[361]; 211 | 212 | int count = 0; 213 | for (int i = hue.length - 1; i >= 0; i--, count++) { 214 | hue[count] = Color.HSVToColor(new float[]{i, 1f, 1f}); 215 | } 216 | 217 | return hue; 218 | } 219 | 220 | 221 | @Override 222 | protected void onDraw(Canvas canvas) { 223 | 224 | if (mDrawingRect.width() <= 0 || mDrawingRect.height() <= 0) return; 225 | 226 | drawSatValPanel(canvas); 227 | drawHuePanel(canvas); 228 | drawAlphaPanel(canvas); 229 | 230 | } 231 | 232 | private void drawSatValPanel(Canvas canvas) { 233 | 234 | final RectF rect = mSatValRect; 235 | 236 | if (BORDER_WIDTH_PX > 0) { 237 | mBorderPaint.setColor(mBorderColor); 238 | canvas.drawRect(mDrawingRect.left, mDrawingRect.top, rect.right + BORDER_WIDTH_PX, rect.bottom + BORDER_WIDTH_PX, mBorderPaint); 239 | } 240 | 241 | if (mValShader == null) { 242 | mValShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, 243 | 0xffffffff, 0xff000000, TileMode.CLAMP); 244 | } 245 | 246 | int rgb = Color.HSVToColor(new float[]{mHue, 1f, 1f}); 247 | 248 | mSatShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, 249 | 0xffffffff, rgb, TileMode.CLAMP); 250 | ComposeShader mShader = new ComposeShader(mValShader, mSatShader, PorterDuff.Mode.MULTIPLY); 251 | mSatValPaint.setShader(mShader); 252 | 253 | canvas.drawRect(rect, mSatValPaint); 254 | 255 | Point p = satValToPoint(mSat, mVal); 256 | 257 | mSatValTrackerPaint.setColor(0xff000000); 258 | canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS - 1f * mDensity, mSatValTrackerPaint); 259 | 260 | mSatValTrackerPaint.setColor(0xffdddddd); 261 | canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS, mSatValTrackerPaint); 262 | 263 | } 264 | 265 | private void drawHuePanel(Canvas canvas) { 266 | 267 | final RectF rect = mHueRect; 268 | 269 | if (BORDER_WIDTH_PX > 0) { 270 | mBorderPaint.setColor(mBorderColor); 271 | canvas.drawRect(rect.left - BORDER_WIDTH_PX, 272 | rect.top - BORDER_WIDTH_PX, 273 | rect.right + BORDER_WIDTH_PX, 274 | rect.bottom + BORDER_WIDTH_PX, 275 | mBorderPaint); 276 | } 277 | 278 | if (mHueShader == null) { 279 | mHueShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, buildHueColorArray(), null, TileMode.CLAMP); 280 | mHuePaint.setShader(mHueShader); 281 | } 282 | 283 | canvas.drawRect(rect, mHuePaint); 284 | 285 | float rectHeight = 4 * mDensity / 2; 286 | 287 | Point p = hueToPoint(mHue); 288 | 289 | RectF r = new RectF(); 290 | r.left = rect.left - RECTANGLE_TRACKER_OFFSET; 291 | r.right = rect.right + RECTANGLE_TRACKER_OFFSET; 292 | r.top = p.y - rectHeight; 293 | r.bottom = p.y + rectHeight; 294 | 295 | 296 | canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint); 297 | 298 | } 299 | 300 | private void drawAlphaPanel(Canvas canvas) { 301 | 302 | if (!mShowAlphaPanel || mAlphaRect == null || mAlphaPattern == null) return; 303 | 304 | final RectF rect = mAlphaRect; 305 | 306 | if (BORDER_WIDTH_PX > 0) { 307 | mBorderPaint.setColor(mBorderColor); 308 | canvas.drawRect(rect.left - BORDER_WIDTH_PX, 309 | rect.top - BORDER_WIDTH_PX, 310 | rect.right + BORDER_WIDTH_PX, 311 | rect.bottom + BORDER_WIDTH_PX, 312 | mBorderPaint); 313 | } 314 | 315 | 316 | mAlphaPattern.draw(canvas); 317 | 318 | float[] hsv = new float[]{mHue, mSat, mVal}; 319 | int color = Color.HSVToColor(hsv); 320 | int acolor = Color.HSVToColor(0, hsv); 321 | 322 | mAlphaShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, 323 | color, acolor, TileMode.CLAMP); 324 | 325 | 326 | mAlphaPaint.setShader(mAlphaShader); 327 | 328 | canvas.drawRect(rect, mAlphaPaint); 329 | 330 | if (mAlphaSliderText != null && mAlphaSliderText != "") { 331 | canvas.drawText(mAlphaSliderText, rect.centerX(), rect.centerY() + 4 * mDensity, mAlphaTextPaint); 332 | } 333 | 334 | float rectWidth = 4 * mDensity / 2; 335 | 336 | Point p = alphaToPoint(mAlpha); 337 | 338 | RectF r = new RectF(); 339 | r.left = p.x - rectWidth; 340 | r.right = p.x + rectWidth; 341 | r.top = rect.top - RECTANGLE_TRACKER_OFFSET; 342 | r.bottom = rect.bottom + RECTANGLE_TRACKER_OFFSET; 343 | 344 | canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint); 345 | 346 | } 347 | 348 | 349 | private Point hueToPoint(float hue) { 350 | 351 | final RectF rect = mHueRect; 352 | final float height = rect.height(); 353 | 354 | Point p = new Point(); 355 | 356 | p.y = (int) (height - (hue * height / 360f) + rect.top); 357 | p.x = (int) rect.left; 358 | 359 | return p; 360 | } 361 | 362 | private Point satValToPoint(float sat, float val) { 363 | 364 | final RectF rect = mSatValRect; 365 | final float height = rect.height(); 366 | final float width = rect.width(); 367 | 368 | Point p = new Point(); 369 | 370 | p.x = (int) (sat * width + rect.left); 371 | p.y = (int) ((1f - val) * height + rect.top); 372 | 373 | return p; 374 | } 375 | 376 | private Point alphaToPoint(int alpha) { 377 | 378 | final RectF rect = mAlphaRect; 379 | final float width = rect.width(); 380 | 381 | Point p = new Point(); 382 | 383 | p.x = (int) (width - (alpha * width / 0xff) + rect.left); 384 | p.y = (int) rect.top; 385 | 386 | return p; 387 | 388 | } 389 | 390 | private float[] pointToSatVal(float x, float y) { 391 | 392 | final RectF rect = mSatValRect; 393 | float[] result = new float[2]; 394 | 395 | float width = rect.width(); 396 | float height = rect.height(); 397 | 398 | if (x < rect.left) { 399 | x = 0f; 400 | } else if (x > rect.right) { 401 | x = width; 402 | } else { 403 | x = x - rect.left; 404 | } 405 | 406 | if (y < rect.top) { 407 | y = 0f; 408 | } else if (y > rect.bottom) { 409 | y = height; 410 | } else { 411 | y = y - rect.top; 412 | } 413 | 414 | 415 | result[0] = 1.f / width * x; 416 | result[1] = 1.f - (1.f / height * y); 417 | 418 | return result; 419 | } 420 | 421 | private float pointToHue(float y) { 422 | 423 | final RectF rect = mHueRect; 424 | 425 | float height = rect.height(); 426 | 427 | if (y < rect.top) { 428 | y = 0f; 429 | } else if (y > rect.bottom) { 430 | y = height; 431 | } else { 432 | y = y - rect.top; 433 | } 434 | 435 | return 360f - (y * 360f / height); 436 | } 437 | 438 | private int pointToAlpha(int x) { 439 | 440 | final RectF rect = mAlphaRect; 441 | final int width = (int) rect.width(); 442 | 443 | if (x < rect.left) { 444 | x = 0; 445 | } else if (x > rect.right) { 446 | x = width; 447 | } else { 448 | x = x - (int) rect.left; 449 | } 450 | 451 | return 0xff - (x * 0xff / width); 452 | 453 | } 454 | 455 | 456 | @Override 457 | public boolean onTrackballEvent(MotionEvent event) { 458 | 459 | float x = event.getX(); 460 | float y = event.getY(); 461 | 462 | boolean update = false; 463 | 464 | 465 | if (event.getAction() == MotionEvent.ACTION_MOVE) { 466 | 467 | switch (mLastTouchedPanel) { 468 | 469 | case PANEL_SAT_VAL: 470 | 471 | float sat, val; 472 | 473 | sat = mSat + x / 50f; 474 | val = mVal - y / 50f; 475 | 476 | if (sat < 0f) { 477 | sat = 0f; 478 | } else if (sat > 1f) { 479 | sat = 1f; 480 | } 481 | 482 | if (val < 0f) { 483 | val = 0f; 484 | } else if (val > 1f) { 485 | val = 1f; 486 | } 487 | 488 | mSat = sat; 489 | mVal = val; 490 | 491 | update = true; 492 | 493 | break; 494 | 495 | case PANEL_HUE: 496 | 497 | float hue = mHue - y * 10f; 498 | 499 | if (hue < 0f) { 500 | hue = 0f; 501 | } else if (hue > 360f) { 502 | hue = 360f; 503 | } 504 | 505 | mHue = hue; 506 | 507 | update = true; 508 | 509 | break; 510 | 511 | case PANEL_ALPHA: 512 | 513 | if (!mShowAlphaPanel || mAlphaRect == null) { 514 | update = false; 515 | } else { 516 | 517 | int alpha = (int) (mAlpha - x * 10); 518 | 519 | if (alpha < 0) { 520 | alpha = 0; 521 | } else if (alpha > 0xff) { 522 | alpha = 0xff; 523 | } 524 | 525 | mAlpha = alpha; 526 | 527 | 528 | update = true; 529 | } 530 | 531 | break; 532 | } 533 | 534 | 535 | } 536 | 537 | 538 | if (update) { 539 | 540 | if (mListener != null) { 541 | mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); 542 | } 543 | 544 | invalidate(); 545 | return true; 546 | } 547 | 548 | 549 | return super.onTrackballEvent(event); 550 | } 551 | 552 | @Override 553 | public boolean onTouchEvent(MotionEvent event) { 554 | 555 | boolean update = false; 556 | 557 | switch (event.getAction()) { 558 | 559 | case MotionEvent.ACTION_DOWN: 560 | 561 | mStartTouchPoint = new Point((int) event.getX(), (int) event.getY()); 562 | 563 | update = moveTrackersIfNeeded(event); 564 | 565 | break; 566 | 567 | case MotionEvent.ACTION_MOVE: 568 | 569 | update = moveTrackersIfNeeded(event); 570 | 571 | break; 572 | 573 | case MotionEvent.ACTION_UP: 574 | 575 | mStartTouchPoint = null; 576 | 577 | update = moveTrackersIfNeeded(event); 578 | 579 | break; 580 | 581 | } 582 | 583 | if (update) { 584 | 585 | if (mListener != null) { 586 | mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); 587 | } 588 | 589 | invalidate(); 590 | return true; 591 | } 592 | 593 | 594 | return super.onTouchEvent(event); 595 | } 596 | 597 | private boolean moveTrackersIfNeeded(MotionEvent event) { 598 | 599 | if (mStartTouchPoint == null) return false; 600 | 601 | boolean update = false; 602 | 603 | int startX = mStartTouchPoint.x; 604 | int startY = mStartTouchPoint.y; 605 | 606 | 607 | if (mHueRect.contains(startX, startY)) { 608 | mLastTouchedPanel = PANEL_HUE; 609 | 610 | mHue = pointToHue(event.getY()); 611 | 612 | update = true; 613 | } else if (mSatValRect.contains(startX, startY)) { 614 | 615 | mLastTouchedPanel = PANEL_SAT_VAL; 616 | 617 | float[] result = pointToSatVal(event.getX(), event.getY()); 618 | 619 | mSat = result[0]; 620 | mVal = result[1]; 621 | 622 | update = true; 623 | } else if (mAlphaRect != null && mAlphaRect.contains(startX, startY)) { 624 | 625 | mLastTouchedPanel = PANEL_ALPHA; 626 | 627 | mAlpha = pointToAlpha((int) event.getX()); 628 | 629 | update = true; 630 | } 631 | 632 | 633 | return update; 634 | } 635 | 636 | @Override 637 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 638 | 639 | int width = 0; 640 | int height = 0; 641 | 642 | int widthMode = MeasureSpec.getMode(widthMeasureSpec); 643 | int heightMode = MeasureSpec.getMode(heightMeasureSpec); 644 | 645 | int widthAllowed = MeasureSpec.getSize(widthMeasureSpec); 646 | int heightAllowed = MeasureSpec.getSize(heightMeasureSpec); 647 | 648 | widthAllowed = chooseWidth(widthMode, widthAllowed); 649 | heightAllowed = chooseHeight(heightMode, heightAllowed); 650 | 651 | if (!mShowAlphaPanel) { 652 | 653 | height = (int) (widthAllowed - PANEL_SPACING - HUE_PANEL_WIDTH); 654 | 655 | //If calculated height (based on the width) is more than the allowed height. 656 | if (height > heightAllowed || getTag().equals("landscape")) { 657 | height = heightAllowed; 658 | width = (int) (height + PANEL_SPACING + HUE_PANEL_WIDTH); 659 | } else { 660 | width = widthAllowed; 661 | } 662 | } else { 663 | 664 | width = (int) (heightAllowed - ALPHA_PANEL_HEIGHT + HUE_PANEL_WIDTH); 665 | 666 | if (width > widthAllowed) { 667 | width = widthAllowed; 668 | height = (int) (widthAllowed - HUE_PANEL_WIDTH + ALPHA_PANEL_HEIGHT); 669 | } else { 670 | height = heightAllowed; 671 | } 672 | 673 | } 674 | 675 | setMeasuredDimension(width, height); 676 | } 677 | 678 | private int chooseWidth(int mode, int size) { 679 | if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { 680 | return size; 681 | } else { // (mode == MeasureSpec.UNSPECIFIED) 682 | return getPrefferedWidth(); 683 | } 684 | } 685 | 686 | private int chooseHeight(int mode, int size) { 687 | if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { 688 | return size; 689 | } else { // (mode == MeasureSpec.UNSPECIFIED) 690 | return getPrefferedHeight(); 691 | } 692 | } 693 | 694 | private int getPrefferedWidth() { 695 | 696 | int width = getPrefferedHeight(); 697 | 698 | if (mShowAlphaPanel) { 699 | width -= (PANEL_SPACING + ALPHA_PANEL_HEIGHT); 700 | } 701 | 702 | 703 | return (int) (width + HUE_PANEL_WIDTH + PANEL_SPACING); 704 | 705 | } 706 | 707 | private int getPrefferedHeight() { 708 | 709 | int height = (int) (200 * mDensity); 710 | 711 | if (mShowAlphaPanel) { 712 | height += PANEL_SPACING + ALPHA_PANEL_HEIGHT; 713 | } 714 | 715 | return height; 716 | } 717 | 718 | 719 | @Override 720 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 721 | super.onSizeChanged(w, h, oldw, oldh); 722 | 723 | mDrawingRect = new RectF(); 724 | mDrawingRect.left = mDrawingOffset + getPaddingLeft(); 725 | mDrawingRect.right = w - mDrawingOffset - getPaddingRight(); 726 | mDrawingRect.top = mDrawingOffset + getPaddingTop(); 727 | mDrawingRect.bottom = h - mDrawingOffset - getPaddingBottom(); 728 | 729 | setUpSatValRect(); 730 | setUpHueRect(); 731 | setUpAlphaRect(); 732 | } 733 | 734 | private void setUpSatValRect() { 735 | 736 | final RectF dRect = mDrawingRect; 737 | float panelSide = dRect.height() - BORDER_WIDTH_PX * 2; 738 | 739 | if (mShowAlphaPanel) { 740 | panelSide -= PANEL_SPACING + ALPHA_PANEL_HEIGHT; 741 | } 742 | 743 | float left = dRect.left + BORDER_WIDTH_PX; 744 | float top = dRect.top + BORDER_WIDTH_PX; 745 | float bottom = top + panelSide; 746 | float right = left + panelSide; 747 | 748 | mSatValRect = new RectF(left, top, right, bottom); 749 | } 750 | 751 | private void setUpHueRect() { 752 | final RectF dRect = mDrawingRect; 753 | 754 | float left = dRect.right - HUE_PANEL_WIDTH + BORDER_WIDTH_PX; 755 | float top = dRect.top + BORDER_WIDTH_PX; 756 | float bottom = dRect.bottom - BORDER_WIDTH_PX - (mShowAlphaPanel ? (PANEL_SPACING + ALPHA_PANEL_HEIGHT) : 0); 757 | float right = dRect.right - BORDER_WIDTH_PX; 758 | 759 | mHueRect = new RectF(left, top, right, bottom); 760 | } 761 | 762 | private void setUpAlphaRect() { 763 | 764 | if (!mShowAlphaPanel) return; 765 | 766 | final RectF dRect = mDrawingRect; 767 | 768 | float left = dRect.left + BORDER_WIDTH_PX; 769 | float top = dRect.bottom - ALPHA_PANEL_HEIGHT + BORDER_WIDTH_PX; 770 | float bottom = dRect.bottom - BORDER_WIDTH_PX; 771 | float right = dRect.right - BORDER_WIDTH_PX; 772 | 773 | mAlphaRect = new RectF(left, top, right, bottom); 774 | 775 | mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity)); 776 | mAlphaPattern.setBounds( 777 | Math.round(mAlphaRect.left), 778 | Math.round(mAlphaRect.top), 779 | Math.round(mAlphaRect.right), 780 | Math.round(mAlphaRect.bottom) 781 | ); 782 | 783 | } 784 | 785 | 786 | /** 787 | * Set a OnColorChangedListener to get notified when the color 788 | * selected by the user has changed. 789 | * 790 | * @param listener 791 | */ 792 | public void setOnColorChangedListener(OnColorChangedListener listener) { 793 | mListener = listener; 794 | } 795 | 796 | /** 797 | * Set the color of the border surrounding all panels. 798 | * 799 | * @param color 800 | */ 801 | public void setBorderColor(int color) { 802 | mBorderColor = color; 803 | invalidate(); 804 | } 805 | 806 | /** 807 | * Get the color of the border surrounding all panels. 808 | */ 809 | public int getBorderColor() { 810 | return mBorderColor; 811 | } 812 | 813 | /** 814 | * Get the current color this view is showing. 815 | * 816 | * @return the current color. 817 | */ 818 | public int getColor() { 819 | return Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal}); 820 | } 821 | 822 | /** 823 | * Set the color the view should show. 824 | * 825 | * @param color The color that should be selected. 826 | */ 827 | public void setColor(int color) { 828 | setColor(color, false); 829 | } 830 | 831 | /** 832 | * Set the color this view should show. 833 | * 834 | * @param color The color that should be selected. 835 | * @param callback If you want to get a callback to 836 | * your OnColorChangedListener. 837 | */ 838 | public void setColor(int color, boolean callback) { 839 | 840 | int alpha = Color.alpha(color); 841 | 842 | float[] hsv = new float[3]; 843 | 844 | Color.colorToHSV(color, hsv); 845 | 846 | mAlpha = alpha; 847 | mHue = hsv[0]; 848 | mSat = hsv[1]; 849 | mVal = hsv[2]; 850 | 851 | if (callback && mListener != null) { 852 | mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); 853 | } 854 | 855 | invalidate(); 856 | } 857 | 858 | /** 859 | * Get the drawing offset of the color picker view. 860 | * The drawing offset is the distance from the side of 861 | * a panel to the side of the view minus the padding. 862 | * Useful if you want to have your own panel below showing 863 | * the currently selected color and want to align it perfectly. 864 | * 865 | * @return The offset in pixels. 866 | */ 867 | public float getDrawingOffset() { 868 | return mDrawingOffset; 869 | } 870 | 871 | /** 872 | * Set if the user is allowed to adjust the alpha panel. Default is false. 873 | * If it is set to false no alpha will be set. 874 | * 875 | * @param visible 876 | */ 877 | public void setAlphaSliderVisible(boolean visible) { 878 | 879 | if (mShowAlphaPanel != visible) { 880 | mShowAlphaPanel = visible; 881 | 882 | /* 883 | * Reset all shader to force a recreation. 884 | * Otherwise they will not look right after 885 | * the size of the view has changed. 886 | */ 887 | mValShader = null; 888 | mSatShader = null; 889 | mHueShader = null; 890 | mAlphaShader = null; 891 | 892 | requestLayout(); 893 | } 894 | 895 | } 896 | 897 | public boolean getAlphaSliderVisible() { 898 | return mShowAlphaPanel; 899 | } 900 | 901 | public void setSliderTrackerColor(int color) { 902 | mSliderTrackerColor = color; 903 | 904 | mHueTrackerPaint.setColor(mSliderTrackerColor); 905 | 906 | invalidate(); 907 | } 908 | 909 | public int getSliderTrackerColor() { 910 | return mSliderTrackerColor; 911 | } 912 | 913 | /** 914 | * Set the text that should be shown in the 915 | * alpha slider. Set to null to disable text. 916 | * 917 | * @param res string resource id. 918 | */ 919 | public void setAlphaSliderText(int res) { 920 | String text = getContext().getString(res); 921 | setAlphaSliderText(text); 922 | } 923 | 924 | /** 925 | * Set the text that should be shown in the 926 | * alpha slider. Set to null to disable text. 927 | * 928 | * @param text Text that should be shown. 929 | */ 930 | public void setAlphaSliderText(String text) { 931 | mAlphaSliderText = text; 932 | invalidate(); 933 | } 934 | 935 | /** 936 | * Get the current value of the text 937 | * that will be shown in the alpha 938 | * slider. 939 | * 940 | * @return 941 | */ 942 | public String getAlphaSliderText() { 943 | return mAlphaSliderText; 944 | } 945 | } -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/res/layout-land/dialog_color_picker.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | 25 | 31 | 32 | 37 | 38 | 48 | 49 | 59 | 60 | 65 | 66 | 74 | 75 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/res/layout/dialog_color_picker.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | 25 | 31 | 32 | 39 | 40 | 46 | 47 | 58 | 59 | 60 | 65 | 66 | 71 | 72 | 80 | 81 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /app/libs/ColorPickerPreference/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Color Picker 5 | Press on Color to apply 6 | 7 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/release/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/app/release/app-release.apk -------------------------------------------------------------------------------- /app/release/output.json: -------------------------------------------------------------------------------- 1 | [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":20,"versionName":"2.9","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/BackgroundInstallActivity.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller; 2 | 3 | import android.app.Activity; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.widget.Toast; 7 | 8 | import cn.bavelee.pokeinstaller.apk.APKCommander; 9 | import cn.bavelee.pokeinstaller.apk.ApkInfo; 10 | import cn.bavelee.pokeinstaller.apk.ICommanderCallback; 11 | 12 | public class BackgroundInstallActivity extends Activity implements ICommanderCallback { 13 | 14 | private APKCommander apkCommander; 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | if (getIntent().getData() != null) { 20 | apkCommander = new APKCommander(this, getIntent().getData(), this); 21 | } else { 22 | showToast(getString(R.string.unable_to_install_apk)); 23 | } 24 | } 25 | 26 | @Override 27 | protected void onResume() { 28 | super.onResume(); 29 | finish(); 30 | } 31 | 32 | private void showToast(String text) { 33 | Toast.makeText(BackgroundInstallActivity.this, text, Toast.LENGTH_SHORT).show(); 34 | } 35 | 36 | @Override 37 | public void onStartParseApk(Uri uri) { 38 | showToast(getString(R.string.parsing)); 39 | } 40 | 41 | @Override 42 | public void onApkParsed(ApkInfo apkInfo) { 43 | apkCommander.startInstall(); 44 | } 45 | 46 | @Override 47 | public void onApkPreInstall(ApkInfo apkInfo) { 48 | showToast(getString(R.string.start_install, apkInfo.getApkFile().getPath())); 49 | } 50 | 51 | @Override 52 | public void onApkInstalled(ApkInfo apkInfo, int resultCode) { 53 | if (resultCode == 0) { 54 | showToast(getString(R.string.apk_installed, apkInfo.getAppName())); 55 | if (!apkInfo.isFakePath() && Prefs.getPreference(this).getBoolean("auto_delete", false)) { 56 | Toast.makeText(this, getString(R.string.apk_deleteed, apkInfo.getApkFile().getName()), Toast.LENGTH_SHORT).show(); 57 | } 58 | } else { 59 | showToast(getString(R.string.install_failed, apkInfo.getAppName())); 60 | } 61 | finish(); 62 | } 63 | 64 | @Override 65 | public void onInstallLog(ApkInfo apkInfo, String logText) { 66 | 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/PokeInstallerActivity.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller; 2 | 3 | import android.Manifest; 4 | import android.content.Intent; 5 | import android.graphics.Color; 6 | import android.graphics.Typeface; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.support.annotation.Nullable; 10 | import android.support.v4.app.FragmentActivity; 11 | import android.text.TextUtils; 12 | import android.view.Gravity; 13 | import android.view.View; 14 | import android.view.Window; 15 | import android.view.WindowManager; 16 | import android.widget.ImageView; 17 | import android.widget.LinearLayout; 18 | import android.widget.ProgressBar; 19 | import android.widget.TextView; 20 | import android.widget.Toast; 21 | 22 | import com.github.florent37.runtimepermission.RuntimePermission; 23 | import com.github.florent37.runtimepermission.callbacks.PermissionListener; 24 | 25 | import java.util.List; 26 | 27 | import cn.bavelee.pokeinstaller.apk.APKCommander; 28 | import cn.bavelee.pokeinstaller.apk.ApkInfo; 29 | import cn.bavelee.pokeinstaller.apk.ICommanderCallback; 30 | 31 | public class PokeInstallerActivity extends FragmentActivity implements ICommanderCallback, View.OnClickListener { 32 | 33 | private TextView tvAppName; 34 | private LinearLayout layoutAppDetails; 35 | private ImageView imgAppIcon; 36 | 37 | private ProgressBar progressBar; 38 | private LinearLayout layoutTitleContainer; 39 | private LinearLayout layoutPermissionList; 40 | private LinearLayout layoutButtons; 41 | 42 | private TextView btnInstall; 43 | private TextView btnSilently; 44 | private TextView btnCancel; 45 | 46 | 47 | private APKCommander apkCommander; 48 | 49 | private int[] settingsColors = new int[4]; 50 | 51 | 52 | @Override 53 | protected void onCreate(Bundle savedInstanceState) { 54 | super.onCreate(savedInstanceState); 55 | setContentView(R.layout.act_poke_installer); 56 | layoutAppDetails = findViewById(R.id.layout_app_details); 57 | tvAppName = findViewById(R.id.tv_app_name); 58 | layoutTitleContainer = findViewById(R.id.titleBar); 59 | imgAppIcon = findViewById(R.id.img_app_icon); 60 | progressBar = findViewById(R.id.progressBar); 61 | progressBar.setVisibility(View.INVISIBLE); 62 | 63 | btnInstall = findViewById(R.id.btn_install); 64 | btnSilently = findViewById(R.id.btn_silently); 65 | btnCancel = findViewById(R.id.btn_cancel); 66 | layoutButtons = (LinearLayout) btnInstall.getParent(); 67 | btnInstall.setEnabled(true); 68 | btnInstall.setOnClickListener(this); 69 | btnSilently.setOnClickListener(this); 70 | btnCancel.setOnClickListener(this); 71 | 72 | loadSettings(); 73 | if (getIntent().getData() == null) { 74 | finish(); 75 | } else 76 | checkPermission(); 77 | } 78 | 79 | private void loadSettings() { 80 | findViewById(R.id.ic_settings).setAlpha(Prefs.getPreference(this).getBoolean("show_settings", true) ? 1 : 0); 81 | findViewById(R.id.ic_settings).setOnClickListener(new View.OnClickListener() { 82 | @Override 83 | public void onClick(View v) { 84 | Intent intent = new Intent(PokeInstallerActivity.this, SettingsActivity.class); 85 | startActivityForResult(intent, 100); 86 | } 87 | }); 88 | 89 | settingsColors[0] = getColor("progress_normal_color"); 90 | settingsColors[1] = getColor("progress_installing_color"); 91 | settingsColors[2] = getColor("progress_success_color"); 92 | settingsColors[3] = getColor("progress_failure_color"); 93 | LinearLayout.LayoutParams marginParams = (LinearLayout.LayoutParams) btnInstall.getLayoutParams(); 94 | marginParams.setMargins(Integer.parseInt(Prefs.getPreference(this).getString("btn_margins", "0")), 0, 0, 0); 95 | btnInstall.setLayoutParams(marginParams); 96 | btnSilently.setLayoutParams(marginParams); 97 | Window window = getWindow(); 98 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 99 | window.setStatusBarColor(getColor("status_bar_color")); 100 | layoutTitleContainer.setBackgroundColor(settingsColors[0]); 101 | if (apkCommander != null && apkCommander.getApkInfo() != null && apkCommander.getApkInfo().getApkFile() != null) { 102 | initDetails(apkCommander.getApkInfo()); 103 | } 104 | } 105 | 106 | @Override 107 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 108 | super.onActivityResult(requestCode, resultCode, data); 109 | loadSettings(); 110 | } 111 | 112 | private void checkPermission() { 113 | RuntimePermission.askPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) 114 | .ask(new PermissionListener() { 115 | @Override 116 | public void onAccepted(RuntimePermission runtimePermission, List accepted) { 117 | apkCommander = new APKCommander(PokeInstallerActivity.this, getIntent().getData(), PokeInstallerActivity.this); 118 | } 119 | 120 | @Override 121 | public void onDenied(RuntimePermission runtimePermission, List denied, List foreverDenied) { 122 | Toast.makeText(PokeInstallerActivity.this, R.string.no_permissions, Toast.LENGTH_SHORT).show(); 123 | finish(); 124 | } 125 | }); 126 | } 127 | 128 | private int getColor(String key) { 129 | return Prefs.getPreference(this).getInt(key, getResources().getColor(R.color.colorPrimary)); 130 | } 131 | 132 | private void initDetails(ApkInfo apkInfo) { 133 | layoutAppDetails.removeAllViews(); 134 | tvAppName.setText(apkInfo.getAppName()); 135 | imgAppIcon.setImageDrawable(apkInfo.getIcon()); 136 | layoutAppDetails.addView(createAppInfoView(getString(R.string.info_pkg_name), apkInfo.getPackageName())); 137 | layoutAppDetails.addView(createAppInfoView(getString(R.string.info_apk_path), apkInfo.getApkFile().getPath())); 138 | layoutAppDetails.addView(createAppInfoView(getString(R.string.info_version), apkInfo.getVersion())); 139 | if (apkInfo.hasInstalledApp()) 140 | layoutAppDetails.addView(createAppInfoView(getString(R.string.info_installed_version), apkInfo.getInstalledVersion())); 141 | if (Prefs.getPreference(this).getBoolean("show_perm", true)) { 142 | if (apkInfo.getPermissions() != null && apkInfo.getPermissions().length > 0) { 143 | layoutPermissionList = new LinearLayout(this); 144 | layoutPermissionList.setOrientation(LinearLayout.VERTICAL); 145 | layoutPermissionList.addView(createAppInfoView(null, getString(R.string.app_permissions))); 146 | for (String perm : apkInfo.getPermissions()) { 147 | layoutPermissionList.addView(createAppPermissionView(perm)); 148 | } 149 | layoutAppDetails.addView(layoutPermissionList); 150 | } 151 | } 152 | } 153 | 154 | private LinearLayout createAppPermissionView(String perm) { 155 | LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.info_item_permission, null, false); 156 | TextView tv1 = (TextView) layout.getChildAt(0); 157 | tv1.setText(perm); 158 | tv1.setTextColor(getColor("perm_color")); 159 | return layout; 160 | } 161 | 162 | 163 | private LinearLayout createAppInfoView(String key, String value) { 164 | LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.info_item, null, false); 165 | TextView tv1 = (TextView) layout.getChildAt(0); 166 | TextView tv2 = (TextView) layout.getChildAt(1); 167 | tv1.setText(key); 168 | tv2.setText(value); 169 | if (TextUtils.isEmpty(value)) { 170 | layout.removeView(tv2); 171 | tv1.setTypeface(Typeface.MONOSPACE); 172 | tv1.setGravity(Gravity.START); 173 | } 174 | if (TextUtils.isEmpty(key)) { 175 | layout.removeView(tv2); 176 | tv1.setText(value); 177 | } 178 | return layout; 179 | } 180 | 181 | @Override 182 | protected void onDestroy() { 183 | super.onDestroy(); 184 | if (apkCommander.getApkInfo() != null && apkCommander.getApkInfo().isFakePath()) 185 | apkCommander.getApkInfo().getApkFile().delete(); 186 | } 187 | 188 | 189 | @Override 190 | public void onStartParseApk(Uri uri) { 191 | TextView textView = new TextView(this); 192 | textView.setTextColor(Color.RED); 193 | textView.setText(getString(R.string.parsing) + " : " + uri.toString()); 194 | layoutAppDetails.addView(textView); 195 | btnInstall.setVisibility(View.GONE); 196 | } 197 | 198 | @Override 199 | public void onApkParsed(ApkInfo apkInfo) { 200 | if (apkInfo != null && !TextUtils.isEmpty(apkInfo.getPackageName())) { 201 | initDetails(apkInfo); 202 | btnInstall.setVisibility(View.VISIBLE); 203 | } else { 204 | Uri uri = getIntent().getData(); 205 | String s = null; 206 | if (uri != null) 207 | s = uri.toString(); 208 | TextView textView = new TextView(this); 209 | textView.setTextColor(Color.RED); 210 | textView.setText(getString(R.string.parse_apk_failed, s)); 211 | layoutAppDetails.addView(textView); 212 | } 213 | } 214 | 215 | @Override 216 | public void onApkPreInstall(ApkInfo apkInfo) { 217 | if (layoutPermissionList != null) 218 | layoutAppDetails.removeView(layoutPermissionList); 219 | tvAppName.setText(R.string.installing); 220 | btnInstall.setEnabled(false); 221 | btnSilently.setEnabled(false); 222 | progressBar.setVisibility( 223 | Prefs.getPreference(this).getBoolean("show_progress_bar", true) ? 224 | View.VISIBLE : View.INVISIBLE); 225 | layoutTitleContainer.setBackgroundColor(settingsColors[1]); 226 | layoutButtons.setVisibility(View.INVISIBLE); 227 | } 228 | 229 | @Override 230 | public void onApkInstalled(ApkInfo apkInfo, int resultCode) { 231 | getString(R.string.install_finished_with_result_code, resultCode); 232 | btnInstall.setEnabled(false); 233 | btnSilently.setEnabled(false); 234 | layoutTitleContainer.setBackgroundColor(settingsColors[0]); 235 | if (resultCode == 0) { 236 | Toast.makeText(getApplicationContext(), getString(R.string.apk_installed, apkInfo.getAppName()), Toast.LENGTH_SHORT).show(); 237 | tvAppName.setText(R.string.successful); 238 | btnInstall.setEnabled(true); 239 | btnInstall.setText(R.string.open_app); 240 | layoutTitleContainer.setBackgroundColor(settingsColors[2]); 241 | if (!apkInfo.isFakePath() && Prefs.getPreference(this).getBoolean("auto_delete", false)) { 242 | Toast.makeText(this, getString(R.string.apk_deleteed, apkInfo.getApkFile().getName()), Toast.LENGTH_SHORT).show(); 243 | } 244 | } else { 245 | tvAppName.setText(R.string.failed); 246 | layoutTitleContainer.setBackgroundColor(settingsColors[3]); 247 | } 248 | progressBar.setVisibility(View.INVISIBLE); 249 | layoutButtons.setVisibility(View.VISIBLE); 250 | } 251 | 252 | @Override 253 | public void onInstallLog(ApkInfo apkInfo, String logText) { 254 | layoutAppDetails.addView(createAppInfoView(logText, null)); 255 | } 256 | 257 | @Override 258 | public void onClick(View v) { 259 | switch (v.getId()) { 260 | case R.id.btn_install: 261 | if (btnInstall.getText().toString().equalsIgnoreCase(getString(R.string.open_app))) { 262 | Intent intent = getPackageManager().getLaunchIntentForPackage(apkCommander.getApkInfo().getPackageName()); 263 | startActivity(intent); 264 | finish(); 265 | } else { 266 | apkCommander.startInstall(); 267 | } 268 | break; 269 | case R.id.btn_silently: 270 | Intent intent = new Intent(this, BackgroundInstallActivity.class); 271 | intent.setData(getIntent().getData()); 272 | startActivity(intent); 273 | finish(); 274 | break; 275 | case R.id.btn_cancel: 276 | finish(); 277 | break; 278 | } 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/Prefs.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.preference.PreferenceManager; 6 | 7 | public class Prefs { 8 | public static SharedPreferences getPreference(Context context) { 9 | return PreferenceManager.getDefaultSharedPreferences(context); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller; 2 | 3 | import android.os.Bundle; 4 | import android.preference.Preference; 5 | import android.preference.PreferenceActivity; 6 | 7 | import cn.bavelee.donatedialog.DonateToMe; 8 | 9 | public class SettingsActivity extends PreferenceActivity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | addPreferencesFromResource(R.xml.prefs); 15 | findPreference("donate").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 16 | @Override 17 | public boolean onPreferenceClick(Preference preference) { 18 | DonateToMe.show(SettingsActivity.this); 19 | return false; 20 | } 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/ShellUtils.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller; 2 | // Copyright (C) 2018 Bave Lee 3 | // This file is part of Quick-Android. 4 | // https://github.com/Crixec/Quick-Android 5 | // 6 | // Quick-Android is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // Quick-Android is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | 18 | import java.io.BufferedReader; 19 | import java.io.Closeable; 20 | import java.io.DataOutputStream; 21 | import java.io.IOException; 22 | import java.io.InputStreamReader; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | public class ShellUtils { 27 | 28 | private static int exec(final String sh, final List cmds, final Result result) { 29 | Process process; 30 | DataOutputStream stdin = null; 31 | OutputReader stdout = null; 32 | OutputReader stderr = null; 33 | int resultCode = -1; 34 | try { 35 | process = Runtime.getRuntime().exec(sh); 36 | stdin = new DataOutputStream(process.getOutputStream()); 37 | if (result != null) { 38 | stdout = new OutputReader(new BufferedReader(new InputStreamReader(process.getInputStream())), 39 | new Output() { 40 | @Override 41 | public void output(String text) { 42 | result.onStdout(text); 43 | } 44 | }); 45 | stderr = new OutputReader(new BufferedReader(new InputStreamReader(process.getErrorStream())), 46 | new Output() { 47 | @Override 48 | public void output(String text) { 49 | result.onStderr(text); 50 | } 51 | }); 52 | stdout.start(); 53 | stderr.start(); 54 | } 55 | for (String cmd : cmds) { 56 | if (result != null) { 57 | result.onCommand(cmd); 58 | } 59 | stdin.write(cmd.getBytes()); 60 | stdin.writeBytes("\n"); 61 | stdin.flush(); 62 | } 63 | stdin.writeBytes("exit $?\n"); 64 | stdin.flush(); 65 | resultCode = process.waitFor(); 66 | if (result != null) { 67 | result.onFinish(resultCode); 68 | } 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } finally { 72 | safeCancel(stderr); 73 | safeCancel(stdout); 74 | safeClose(stdout); 75 | safeClose(stderr); 76 | safeClose(stdin); 77 | } 78 | return resultCode; 79 | } 80 | 81 | private static void safeCancel(OutputReader reader) { 82 | try { 83 | if (reader != null) reader.cancel(); 84 | } catch (Exception ignored) { 85 | 86 | } 87 | } 88 | 89 | private static void safeClose(Closeable closeable) { 90 | try { 91 | if (closeable != null) closeable.close(); 92 | } catch (Exception ignored) { 93 | 94 | } 95 | } 96 | 97 | public static int exec(final List cmds, final Result result, final boolean isRoot) { 98 | String sh = isRoot ? "su" : "sh"; 99 | return exec(sh, cmds, result); 100 | } 101 | 102 | public static int exec(final List cmds, final boolean isRoot) { 103 | return exec(cmds, null, isRoot); 104 | } 105 | 106 | public static int exec(final String cmd, boolean isRoot) { 107 | return exec(cmd, null, isRoot); 108 | } 109 | 110 | public static int exec(final String cmd, final Result result, boolean isRoot) { 111 | List cmds = new ArrayList(); 112 | cmds.add(cmd); 113 | return exec(cmds, result, isRoot); 114 | } 115 | 116 | public static int exec(final String cmd) { 117 | return exec(cmd, null, false); 118 | } 119 | 120 | public static int execWithRoot(final String cmd) { 121 | return exec(cmd, null, true); 122 | } 123 | 124 | public static int execWithRoot(final String cmd, final Result result) { 125 | return exec(cmd, result, true); 126 | } 127 | 128 | public interface Result { 129 | void onStdout(String text); 130 | 131 | void onStderr(String text); 132 | 133 | void onCommand(String command); 134 | 135 | void onFinish(int resultCode); 136 | } 137 | 138 | private interface Output { 139 | void output(String text); 140 | } 141 | 142 | public static class OutputReader extends Thread implements Closeable { 143 | private Output output = null; 144 | private BufferedReader reader = null; 145 | private boolean isRunning = false; 146 | 147 | private OutputReader(BufferedReader reader, Output output) { 148 | this.output = output; 149 | this.reader = reader; 150 | this.isRunning = true; 151 | } 152 | 153 | @Override 154 | public void close() { 155 | try { 156 | reader.close(); 157 | } catch (IOException ignored) { 158 | } 159 | } 160 | 161 | @Override 162 | public void run() { 163 | super.run(); 164 | String line; 165 | while (isRunning) { 166 | try { 167 | line = reader.readLine(); 168 | if (line != null) 169 | output.output(line); 170 | } catch (IOException ignored) { 171 | } 172 | } 173 | } 174 | 175 | private void cancel() { 176 | synchronized (this) { 177 | isRunning = false; 178 | this.notifyAll(); 179 | } 180 | } 181 | } 182 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/apk/APKCommander.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller.apk; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.net.Uri; 7 | import android.os.Handler; 8 | import android.os.Looper; 9 | import android.util.AndroidRuntimeException; 10 | 11 | import java.io.File; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.OutputStream; 16 | 17 | import cn.bavelee.pokeinstaller.ShellUtils; 18 | 19 | public class APKCommander { 20 | 21 | private Context context; 22 | private Uri uri; 23 | private ApkInfo mApkInfo; 24 | private ICommanderCallback callback; 25 | private Handler handler; 26 | 27 | public APKCommander(Context context, Uri uri, ICommanderCallback commanderCallback) { 28 | this.context = context; 29 | this.uri = uri; 30 | this.callback = commanderCallback; 31 | handler = new Handler(Looper.getMainLooper()); 32 | new ParseApkTask().start(); 33 | } 34 | 35 | public void startInstall() { 36 | new InstallApkTask().start(); 37 | } 38 | 39 | public ApkInfo getApkInfo() { 40 | return mApkInfo; 41 | } 42 | 43 | private class InstallApkTask extends Thread { 44 | @Override 45 | public void run() { 46 | super.run(); 47 | handler.post(new Runnable() { 48 | @Override 49 | public void run() { 50 | callback.onApkPreInstall(mApkInfo); 51 | } 52 | }); 53 | final int retCode = ShellUtils.execWithRoot("setenforce 0 && pm install -r --user 0 \"" + mApkInfo.getApkFile().getPath() + "\"" + "\n", new ShellUtils.Result() { 54 | @Override 55 | public void onStdout(final String text) { 56 | handler.post(new Runnable() { 57 | @Override 58 | public void run() { 59 | callback.onInstallLog(mApkInfo, text); 60 | } 61 | }); 62 | } 63 | 64 | @Override 65 | public void onStderr(final String text) { 66 | handler.post(new Runnable() { 67 | @Override 68 | public void run() { 69 | callback.onInstallLog(mApkInfo, text); 70 | } 71 | }); 72 | } 73 | 74 | @Override 75 | public void onCommand(String command) { 76 | 77 | } 78 | 79 | @Override 80 | public void onFinish(int resultCode) { 81 | 82 | } 83 | }); 84 | if (retCode == 0 && mApkInfo.isFakePath()) 85 | mApkInfo.getApkFile().delete(); 86 | handler.post(new Runnable() { 87 | @Override 88 | public void run() { 89 | callback.onApkInstalled(mApkInfo, retCode); 90 | } 91 | }); 92 | } 93 | } 94 | 95 | private class ParseApkTask extends Thread { 96 | @Override 97 | public void run() { 98 | super.run(); 99 | try { 100 | handler.post(new Runnable() { 101 | @Override 102 | public void run() { 103 | callback.onStartParseApk(uri); 104 | } 105 | }); 106 | mApkInfo = new ApkInfo(); 107 | String apkSourcePath = ContentUriUtils.getPath(context, uri); 108 | if (apkSourcePath == null) { 109 | mApkInfo.setFakePath(true); 110 | File tempFile = new File(context.getExternalCacheDir(), System.currentTimeMillis() + ".apk"); 111 | try { 112 | InputStream is = context.getContentResolver().openInputStream(uri); 113 | if (is != null) { 114 | OutputStream fos = new FileOutputStream(tempFile); 115 | byte[] buf = new byte[4096 * 1024]; 116 | int ret; 117 | while ((ret = is.read(buf)) != -1) { 118 | fos.write(buf, 0, ret); 119 | fos.flush(); 120 | } 121 | fos.close(); 122 | is.close(); 123 | } 124 | } catch (IOException e) { 125 | e.printStackTrace(); 126 | } 127 | mApkInfo.setApkFile(tempFile); 128 | } else { 129 | mApkInfo.setApkFile(new File(apkSourcePath)); 130 | } 131 | //读取apk的信息 132 | PackageManager pm = context.getPackageManager(); 133 | PackageInfo pkgInfo = pm.getPackageArchiveInfo(mApkInfo.getApkFile().getPath(), PackageManager.GET_PERMISSIONS); 134 | if (pkgInfo != null) { 135 | pkgInfo.applicationInfo.sourceDir = mApkInfo.getApkFile().getPath(); 136 | pkgInfo.applicationInfo.publicSourceDir = mApkInfo.getApkFile().getPath(); 137 | mApkInfo.setAppName(pm.getApplicationLabel(pkgInfo.applicationInfo).toString()); 138 | mApkInfo.setPackageName(pkgInfo.applicationInfo.packageName); 139 | mApkInfo.setVersionName(pkgInfo.versionName); 140 | mApkInfo.setVersionCode(pkgInfo.versionCode); 141 | mApkInfo.setIcon(pkgInfo.applicationInfo.loadIcon(pm)); 142 | try { 143 | PackageInfo installedPkgInfo = pm.getPackageInfo(mApkInfo.getPackageName(), 0); 144 | mApkInfo.setInstalledVersionName(installedPkgInfo.versionName); 145 | mApkInfo.setInstalledVersionCode(installedPkgInfo.versionCode); 146 | mApkInfo.setHasInstalledApp(true); 147 | } catch (PackageManager.NameNotFoundException e) { 148 | e.printStackTrace(); 149 | mApkInfo.setHasInstalledApp(false); 150 | } 151 | mApkInfo.setPermissions(pkgInfo.requestedPermissions); 152 | } 153 | handler.post(new Runnable() { 154 | @Override 155 | public void run() { 156 | callback.onApkParsed(mApkInfo); 157 | } 158 | }); 159 | } catch (Exception e) { 160 | handler.post(new Runnable() { 161 | @Override 162 | public void run() { 163 | callback.onApkParsed(null); 164 | } 165 | }); 166 | e.printStackTrace(); 167 | throw new AndroidRuntimeException(e); 168 | 169 | } 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/apk/ApkInfo.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller.apk; 2 | 3 | import android.graphics.drawable.Drawable; 4 | 5 | import java.io.File; 6 | 7 | /** 8 | * Created by Bave on 2018/2/2. 9 | */ 10 | 11 | public class ApkInfo { 12 | private File apkFile; 13 | private String appName; 14 | private Drawable icon; 15 | private String versionName; 16 | private int versionCode; 17 | private String packageName; 18 | private boolean hasInstalledApp; 19 | private String installedVersionName; 20 | private int installedVersionCode; 21 | private boolean isFakePath; 22 | private String[] permissions; 23 | 24 | public boolean isHasInstalledApp() { 25 | return hasInstalledApp; 26 | } 27 | 28 | public String[] getPermissions() { 29 | return permissions; 30 | } 31 | 32 | public void setPermissions(String[] permissions) { 33 | this.permissions = permissions; 34 | } 35 | 36 | public void setApkFile(File apkFile) { 37 | this.apkFile = apkFile; 38 | } 39 | 40 | public boolean isFakePath() { 41 | return isFakePath; 42 | } 43 | 44 | public void setFakePath(boolean fakePath) { 45 | isFakePath = fakePath; 46 | } 47 | 48 | public String getVersionName() { 49 | return versionName; 50 | } 51 | 52 | public void setVersionName(String versionName) { 53 | this.versionName = versionName; 54 | } 55 | 56 | public int getVersionCode() { 57 | return versionCode; 58 | } 59 | 60 | public void setVersionCode(int versionCode) { 61 | this.versionCode = versionCode; 62 | } 63 | 64 | public String getPackageName() { 65 | return packageName; 66 | } 67 | 68 | public String getVersion() { 69 | return versionName + "(" + versionCode + ")"; 70 | } 71 | 72 | public String getInstalledVersion() { 73 | return hasInstalledApp ? installedVersionName + "(" + installedVersionCode + ")" : "NO"; 74 | } 75 | 76 | public void setPackageName(String packageName) { 77 | this.packageName = packageName; 78 | } 79 | 80 | public boolean hasInstalledApp() { 81 | return hasInstalledApp; 82 | } 83 | 84 | public void setHasInstalledApp(boolean hasInstalledApp) { 85 | this.hasInstalledApp = hasInstalledApp; 86 | } 87 | 88 | public String getInstalledVersionName() { 89 | return installedVersionName; 90 | } 91 | 92 | public void setInstalledVersionName(String installedVersionName) { 93 | this.installedVersionName = installedVersionName; 94 | } 95 | 96 | public int getInstalledVersionCode() { 97 | return installedVersionCode; 98 | } 99 | 100 | public void setInstalledVersionCode(int installedVersionCode) { 101 | this.installedVersionCode = installedVersionCode; 102 | } 103 | 104 | public String getAppName() { 105 | return appName; 106 | } 107 | 108 | public Drawable getIcon() { 109 | return icon; 110 | } 111 | 112 | public void setIcon(Drawable icon) { 113 | this.icon = icon; 114 | } 115 | 116 | public void setAppName(String appName) { 117 | this.appName = appName; 118 | } 119 | 120 | public File getApkFile() { 121 | return apkFile; 122 | } 123 | 124 | public String getFileName() { 125 | return apkFile == null ? null : apkFile.getName(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/apk/ContentUriUtils.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller.apk; 2 | 3 | 4 | import android.content.ContentUris; 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.database.DatabaseUtils; 8 | import android.net.Uri; 9 | import android.os.Environment; 10 | import android.provider.DocumentsContract; 11 | import android.provider.MediaStore; 12 | 13 | public class ContentUriUtils { 14 | //修改自https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java 15 | //原帖:https://stackoverflow.com/questions/19985286/convert-content-uri-to-actual-path-in-android-4-4 16 | 17 | /** 18 | * Get a file path from a Uri. This will get the the path for Storage Access 19 | * Framework Documents, as well as the _data field for the MediaStore and 20 | * other file-based ContentProviders.
21 | *
22 | * Callers should check whether the path is local before assuming it 23 | * represents a local file. 24 | * 25 | * @param context The context. 26 | * @param uri The Uri to query. 27 | * @author paulburke 28 | */ 29 | public static String getPath(final Context context, final Uri uri) { 30 | 31 | // DocumentProvider 32 | if (DocumentsContract.isDocumentUri(context, uri)) { 33 | // ExternalStorageProvider 34 | if (isExternalStorageDocument(uri)) { 35 | final String docId = DocumentsContract.getDocumentId(uri); 36 | final String[] split = docId.split(":"); 37 | final String type = split[0]; 38 | 39 | if ("primary".equalsIgnoreCase(type)) { 40 | return Environment.getExternalStorageDirectory() + "/" + split[1]; 41 | } 42 | 43 | } 44 | // DownloadsProvider 45 | else if (isDownloadsDocument(uri)) { 46 | 47 | final String id = DocumentsContract.getDocumentId(uri); 48 | final Uri contentUri = ContentUris.withAppendedId( 49 | Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); 50 | 51 | return getDataColumn(context, contentUri, null, null); 52 | } 53 | // MediaProvider 54 | else if (isMediaDocument(uri)) { 55 | final String docId = DocumentsContract.getDocumentId(uri); 56 | final String[] split = docId.split(":"); 57 | final String type = split[0]; 58 | 59 | Uri contentUri = null; 60 | if ("image".equals(type)) { 61 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 62 | } else if ("video".equals(type)) { 63 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 64 | } else if ("audio".equals(type)) { 65 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 66 | } 67 | 68 | final String selection = "_id=?"; 69 | final String[] selectionArgs = new String[]{ 70 | split[1] 71 | }; 72 | 73 | return getDataColumn(context, contentUri, selection, selectionArgs); 74 | } 75 | } 76 | // MediaStore (and general) 77 | else if ("content".equalsIgnoreCase(uri.getScheme())) { 78 | 79 | // Return the remote address 80 | if (isGooglePhotosUri(uri)) 81 | return uri.getLastPathSegment(); 82 | 83 | return getDataColumn(context, uri, null, null); 84 | } 85 | // File 86 | else if ("file".equalsIgnoreCase(uri.getScheme())) { 87 | return uri.getPath(); 88 | } 89 | 90 | return null; 91 | } 92 | 93 | /** 94 | * @param uri The Uri to check. 95 | * @return Whether the Uri authority is ExternalStorageProvider. 96 | * @author paulburke 97 | */ 98 | private static boolean isExternalStorageDocument(Uri uri) { 99 | return "com.android.externalstorage.documents".equals(uri.getAuthority()); 100 | } 101 | 102 | /** 103 | * @param uri The Uri to check. 104 | * @return Whether the Uri authority is DownloadsProvider. 105 | * @author paulburke 106 | */ 107 | private static boolean isDownloadsDocument(Uri uri) { 108 | return "com.android.providers.downloads.documents".equals(uri.getAuthority()); 109 | } 110 | 111 | /** 112 | * @param uri The Uri to check. 113 | * @return Whether the Uri authority is MediaProvider. 114 | * @author paulburke 115 | */ 116 | private static boolean isMediaDocument(Uri uri) { 117 | return "com.android.providers.media.documents".equals(uri.getAuthority()); 118 | } 119 | 120 | /** 121 | * @param uri The Uri to check. 122 | * @return Whether the Uri authority is Google Photos. 123 | */ 124 | private static boolean isGooglePhotosUri(Uri uri) { 125 | return "com.google.android.apps.photos.content".equals(uri.getAuthority()); 126 | } 127 | 128 | 129 | /** 130 | * Get the value of the data column for this Uri. This is useful for 131 | * MediaStore Uris, and other file-based ContentProviders. 132 | * 133 | * @param context The context. 134 | * @param uri The Uri to query. 135 | * @param selection (Optional) Filter used in the query. 136 | * @param selectionArgs (Optional) Selection arguments used in the query. 137 | * @return The value of the _data column, which is typically a file path. 138 | * @author paulburke 139 | */ 140 | private static String getDataColumn(Context context, Uri uri, String selection, 141 | String[] selectionArgs) { 142 | 143 | Cursor cursor = null; 144 | final String column = MediaStore.MediaColumns.DATA; 145 | final String[] projection = { 146 | column 147 | }; 148 | 149 | try { 150 | cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, 151 | null); 152 | if (cursor != null && cursor.moveToFirst()) { 153 | DatabaseUtils.dumpCursor(cursor); 154 | final int column_index = cursor.getColumnIndexOrThrow(column); 155 | return cursor.getString(column_index); 156 | } 157 | } catch (Exception e) { 158 | e.printStackTrace(); 159 | } finally { 160 | if (cursor != null) 161 | cursor.close(); 162 | } 163 | return null; 164 | } 165 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/bavelee/pokeinstaller/apk/ICommanderCallback.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.pokeinstaller.apk; 2 | 3 | import android.net.Uri; 4 | 5 | import cn.bavelee.pokeinstaller.apk.ApkInfo; 6 | 7 | public interface ICommanderCallback { 8 | void onStartParseApk(Uri uri); 9 | 10 | void onApkParsed(ApkInfo apkInfo); 11 | 12 | void onApkPreInstall(ApkInfo apkInfo); 13 | 14 | void onApkInstalled(ApkInfo apkInfo, int resultCode); 15 | 16 | void onInstallLog(ApkInfo apkInfo, String logText); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_hover.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/act_poke_installer.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 22 | 23 | 28 | 29 | 30 | 35 | 36 | 45 | 46 | 47 | 48 | 56 | 57 | 63 | 64 | 71 | 72 | 77 | 78 | 79 | 80 | 81 | 87 | 88 | 97 | 98 | 107 | 108 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /app/src/main/res/layout/info_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/info_item_permission.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Poke 安裝器 4 | 安裝中… 5 | 安裝 6 | 無法解析 %s 7 | 背景安裝 8 | 解析中… 9 | %s 安裝失敗 10 | %s 已安裝 11 | 開始安裝 %s 12 | 安裝返回值 : %d 13 | 檔案位置 : 14 | 應用名稱 : 15 | 套件名稱 : 16 | 版本訊息 : 17 | 目前版本 : 18 | 安裝成功 19 | 安裝失败 20 | 返回 21 | 22 | 23 | 背景安裝 24 | 無法安裝應用 25 | 打開 26 | 顯示設置按鈕 27 | 是否在安裝界面顯示設置按鈕(不可見但是可以點擊) 28 | 安裝時顯示進度條 29 | 進度條 30 | 顏色設置 31 | 頂部顏色 32 | 安裝時的頂部背景色 33 | 安裝時的背景色 34 | 安裝失敗後的頂部背景顏色 35 | 失敗 36 | 安裝成功後的頂部背景色 37 | 成功 38 | 狀態欄的顏色 39 | 狀態欄 40 | 強迫症設置 41 | 頂部默認顏色 42 | 捐贈 43 | 捐贈,可以讓我把軟件做的更好 44 | 未授予讀寫權限,退出 45 | 成功安裝後自動刪除安裝包 46 | 自動刪除 47 | %s 已刪除 48 | 應用權限 : 49 | 應用權限 50 | 顯示權限列表 51 | 權限列表的文字顏色 52 | 權限文字 53 | 邊距 54 | 底部按鈕邊距 55 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Poke 安装器 4 | 安装中… 5 | 安装 6 | 无法解析 %s 7 | 静默安装 8 | 解析中… 9 | %s 安装失败 10 | %s 已安装 11 | 开始安装 %s 12 | 安装完成返回 : %d 13 | 应用路径 : 14 | 应用名称 : 15 | 应用包名 : 16 | 版本信息 : 17 | 当前版本 : 18 | 安装成功 19 | 安装失败 20 | 返回 21 | 22 | 23 | 静默安装 24 | 无法安装应用 25 | 打开 26 | 显示设置按钮 27 | 是否在安装界面显示设置按钮(不可见但是可以点击) 28 | 安装时显示进度条 29 | 进度条 30 | 颜色设置 31 | 顶部颜色 32 | 安装时的顶部背景色 33 | 安装时的背景色 34 | 安装失败后的顶部背景色 35 | 失败 36 | 安装成功后的顶部背景色 37 | 成功 38 | 状态栏的颜色 39 | 状态栏 40 | 强迫症设置 41 | 顶部默认颜色 42 | 捐赠 43 | 捐赠,可以让我把软件做的更好 44 | 没权限,不干!!! 45 | 安装成功后自动删除安装包 46 | 自动删除 47 | %s 已删除 48 | "应用权限 : " 49 | 应用权限 50 | 显示权限列表 51 | 权限列表的文字颜色 52 | 权限文字 53 | 边距 54 | 底部按钮边距 55 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0 5 | 16 6 | 32 7 | 48 8 | 64 9 | 80 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #B6B6B6 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 120dp 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Poke Installer 3 | Installing… 4 | Install 5 | Cannot parse apk file :%s 6 | Silently Install Under ROOT 7 | Parsing … 8 | %s install failed 9 | %s has been installed 10 | Start install %s 11 | Installation finished with result code %d 12 | APK PATH : 13 | APP NAME : 14 | PKG NAME : 15 | VERSION : 16 | CUR VER : 17 | Successful 18 | Failed 19 | Back 20 | YES 21 | NO 22 | Silently 23 | Unable to install apk 24 | OPEN 25 | Display Settings Button 26 | Display settings button on install screen(invisible but can be click) 27 | Display progress bar while installing 28 | Display progress bar 29 | OCD Settings 30 | Color Settings 31 | Default background color of top 32 | Default 33 | Background color while installing 34 | Installing 35 | Background color after install failure 36 | Failure 37 | Background color of success 38 | Success 39 | Color of status bar 40 | Status Bar 41 | Donate 42 | Donation can makes this app do better 43 | Did not grant read and write permissions, exit 44 | Automatically remove the installation package after successful installation 45 | Auto Delete 46 | %s has been delete 47 | APP PERM : 48 | App Permissions 49 | Display app permissions list 50 | Text color of permission list 51 | Permission Text 52 | Margins 53 | Margins of bottom buttons 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/xml/prefs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 38 | 39 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.4.1' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed May 01 16:10:53 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /module_donatedialog/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /module_donatedialog/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | targetSdkVersion 28 7 | minSdkVersion 21 8 | versionCode 1 9 | versionName "1.0" 10 | } 11 | buildTypes { 12 | release { 13 | minifyEnabled false 14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 15 | } 16 | } 17 | } 18 | 19 | dependencies { 20 | implementation fileTree(include: ['*.jar'], dir: 'libs') 21 | } 22 | -------------------------------------------------------------------------------- /module_donatedialog/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /module_donatedialog/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /module_donatedialog/src/main/java/cn/bavelee/donatedialog/DonateToMe.java: -------------------------------------------------------------------------------- 1 | package cn.bavelee.donatedialog; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.app.AlertDialog; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.content.pm.PackageManager; 10 | import android.graphics.Bitmap; 11 | import android.net.Uri; 12 | import android.provider.MediaStore; 13 | import android.widget.ImageView; 14 | import android.widget.Toast; 15 | 16 | import java.io.File; 17 | import java.io.FileOutputStream; 18 | import java.io.IOException; 19 | import java.net.URLEncoder; 20 | 21 | /** 22 | * Created by Bave on 2018/1/5. 23 | */ 24 | 25 | public class DonateToMe { 26 | public static void show(final Context context) { 27 | if (context == null) return; 28 | DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() { 29 | @Override 30 | public void onClick(DialogInterface dialogInterface, int i) { 31 | switch (i) { 32 | case DialogInterface.BUTTON_NEGATIVE: 33 | // wechat 34 | showSaveQRCodeDialog(context, R.drawable.wechat_money_revised); 35 | break; 36 | case DialogInterface.BUTTON_POSITIVE: 37 | // alipay 38 | if (haveInstalledAlipay(context)) { 39 | jumpToAlipyScreen(context); 40 | } else { 41 | showSaveQRCodeDialog(context, R.drawable.alipay_money_revised); 42 | } 43 | break; 44 | case DialogInterface.BUTTON_NEUTRAL: 45 | break; 46 | } 47 | } 48 | }; 49 | new AlertDialog.Builder(context).setView(R.layout.layout_donate_dialog) 50 | .setTitle(R.string.title_donate_dialog_donate_methods) 51 | .setNeutralButton(android.R.string.no, onClickListener) 52 | .setPositiveButton(R.string.title_donate_dialog_alipay, onClickListener) 53 | .setNegativeButton(R.string.title_donate_dialog_wechat, onClickListener) 54 | .show(); 55 | 56 | } 57 | 58 | private static void showSaveQRCodeDialog(Context context, int resId) { 59 | final ImageView imageView = new ImageView(context); 60 | imageView.setImageResource(resId); 61 | imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 62 | new AlertDialog.Builder(context).setView(imageView) 63 | .setNeutralButton(android.R.string.cancel, null) 64 | .setPositiveButton(R.string.title_donate_dialog_save_qr_code, new DialogInterface.OnClickListener() { 65 | @Override 66 | public void onClick(DialogInterface dialogInterface, int i) { 67 | saveImage(imageView); 68 | } 69 | }) 70 | .show(); 71 | } 72 | 73 | private static void saveBitmap(Bitmap mBitmap, File file) { 74 | try { 75 | FileOutputStream fos = new FileOutputStream(file); 76 | mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); 77 | fos.flush(); 78 | fos.close(); 79 | } catch (IOException e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | 84 | private static void saveImage(ImageView imageView) { 85 | imageView.setDrawingCacheEnabled(true); 86 | Bitmap bitmap = imageView.getDrawingCache(); 87 | File qrCode = new File(imageView.getContext().getExternalCacheDir(), "qrcode.jpg"); 88 | saveBitmap(bitmap, qrCode); 89 | imageView.setDrawingCacheEnabled(false); 90 | imageView.getContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + qrCode.getAbsolutePath()))); 91 | Toast.makeText(imageView.getContext(), R.string.text_donate_dialog_qr_code_saved, Toast.LENGTH_SHORT).show(); 92 | } 93 | 94 | public static boolean haveInstalledAlipay(Context context) { 95 | try { 96 | return context.getPackageManager().getPackageInfo("com.eg.android.AlipayGphone", PackageManager.GET_ACTIVITIES) != null; 97 | } catch (PackageManager.NameNotFoundException e) { 98 | e.printStackTrace(); 99 | return false; 100 | } 101 | } 102 | 103 | public static void jumpToAlipyScreen(Context context) { 104 | String qrcode = URLEncoder.encode("HTTPS://QR.ALIPAY.COM/FKX05494PUYB5GFV1VNXAD"); 105 | String url = "alipayqr://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=" + qrcode + "%3F_s%3Dweb-other&_t=" + System.currentTimeMillis(); 106 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); 107 | context.startActivity(intent); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /module_donatedialog/src/main/res/drawable/alipay_money_revised.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/module_donatedialog/src/main/res/drawable/alipay_money_revised.png -------------------------------------------------------------------------------- /module_donatedialog/src/main/res/drawable/wechat_money_revised.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/module_donatedialog/src/main/res/drawable/wechat_money_revised.png -------------------------------------------------------------------------------- /module_donatedialog/src/main/res/layout/layout_donate_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 24 | -------------------------------------------------------------------------------- /module_donatedialog/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 捐赠 4 | 嗨,你好,欢迎使用我的app,我是一个独立的个人开发者,独立的个人开发者收入有限。如果你觉得这个程序很有用,可以给我买我一杯咖啡作为鼓励吗,谢谢你的支持。\n 5 | 有任何问题,请联系我 : \n\nbavelee@foxmail.com\n 6 | 支付宝 7 | 微信 8 | 捐赠方式 9 | 二维码已保存 10 | 保存二维码 11 | -------------------------------------------------------------------------------- /module_donatedialog/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DONATE 3 | Hey, guy, welcome to my app.I am a young personal developer, I learning programming by myself, I enjoy writing codes.If you think this app is useful, may you can donate me for a coffee as a encouragement, thanks for your support.\n 4 | If you have any problems, please contact me : \n\nbavelee@foxmail.com\n 5 | Alipay 6 | Wechat 7 | Methods of donation 8 | QR Code saved 9 | Save QR code 10 | 11 | -------------------------------------------------------------------------------- /pokeinstaller.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayae/PokeInstaller/501f0e286189aaa145e50e7fcc46d834c70487c8/pokeinstaller.keystore -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':ColorPickerPreference', 'module_donatedialog' 2 | project(':ColorPickerPreference').projectDir = new File('app/libs/ColorPickerPreference') --------------------------------------------------------------------------------