diff --git a/.gitignore b/.gitignore index c7a0250..baf518f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,10 @@ -library/build -library/library.iml -sample/build -sample/sample.iml -.idea -build -android-auto-update.iml -local.properties \ No newline at end of file +/.gradle/ +/.idea/ +/build/ +/library/build/ +/library/library.iml +/sample/build/ +/sample/sample.iml +/android-auto-update.iml +/local.properties diff --git a/README.md b/README.md index 7d42584..11f9abb 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,38 @@ Android应用自动更新库(android-auto-update) =================== -该library项目实现了软件版本检查,apk文件下载,软件安装(Android app update checker,download and install apk)支持API 8+ +该library项目实现了软件版本检查,apk文件下载,软件安装(Android app update checker,download and install apk)支持API 14+ -#### 1.导入library项目 #### +#### 1.导入library项目 提供2种版本检查方式,在你的项目中添加以下代码即可 - 使用Dialog - `UpdateChecker.checkForDialog(this);` + UpdateChecker.checkForDialog(this); - 使用Notification - `UpdateChecker.checkForNotification(this);` + UpdateChecker.checkForNotification(this); -#### 2.添加权限 #### +#### 2.截图 -- 添加访问网络的权限 - - `` - -- 添加写SDCard权限(可选,非必须) - - 如果添加这个权限 apk下载在sdcard中的Android/data/包名/cache目录下 否则下载到 内存中的 /data/data/包名/cache中 - - `` - -#### 3.截图 #### ![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/sample.png) -![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/sample_htc.png) -![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/dialog.png) -![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/dialog_htc.png) -![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/notification.png) -![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/notification_avd.png) -![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/downloading.png) -![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/downloading_avd.png) +![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/sample_htc.png) -#### 4.使用与参考的开源项目 #### +![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/dialog.png) +![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/dialog_htc.png) +![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/notification.png) -1. [android-styled-dialogs](https://github.com/inmite/android-styled-dialogs "https://github.com/inmite/android-styled-dialogs") 使用该项目,可以在api 8+上显示 holo 风格的对话框,其它选择 -,当然你也可以使用其它的开源项目比如:[ActionBarSherlock](https://github.com/JakeWharton/ActionBarSherlock "https://github.com/JakeWharton/ActionBarSherlock") 和 [HoloEverywhere](https://github.com/Prototik/HoloEverywhere "https://github.com/Prototik/HoloEverywhere") +![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/notification_avd.png) +![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/downloading.png) -2. [UpdateChecker](https://github.com/rampo/UpdateChecker "https://github.com/rampo/UpdateChecker") 该项目检查的是google play上的应用,如果有更新打开google Play,不提供下载apk的功能 +![screenshot](https://raw.github.com/feicien/android-auto-update/master/screenshots/downloading_avd.png) - diff --git a/build.gradle b/build.gradle index 49357e9..7c3502c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,20 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:1.1.0' + classpath 'com.android.tools.build:gradle:3.1.0' } } allprojects { repositories { jcenter() + google() } } + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/extras/android-auto-update-v1.1.apk b/extras/android-auto-update-v1.1.apk new file mode 100644 index 0000000..b70ed8a Binary files /dev/null and b/extras/android-auto-update-v1.1.apk differ diff --git a/extras/android-auto-update-v1.2.apk b/extras/android-auto-update-v1.2.apk new file mode 100644 index 0000000..b2050f8 Binary files /dev/null and b/extras/android-auto-update-v1.2.apk differ diff --git a/extras/android-auto-update-v1.3.apk b/extras/android-auto-update-v1.3.apk new file mode 100644 index 0000000..10b6101 Binary files /dev/null and b/extras/android-auto-update-v1.3.apk differ diff --git a/extras/demo.jks b/extras/demo.jks new file mode 100644 index 0000000..0b01653 Binary files /dev/null and b/extras/demo.jks differ diff --git a/extras/update.json b/extras/update.json new file mode 100644 index 0000000..f77795a --- /dev/null +++ b/extras/update.json @@ -0,0 +1,5 @@ +{ + "url":"https://raw.githubusercontent.com/feicien/android-auto-update/develop/extras/android-auto-update-v1.3.apk", + "versionCode":4, + "updateMessage":"1. 适配 Android 8.0\n2. 适配 Android 9.0\n3. 更新说明" +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c97a8bd..f6b961f 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 51c68cb..6851569 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Feb 28 13:34:31 CST 2015 +#Sat Apr 07 16:35:01 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 91a7e26..cccdd3d --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..f955316 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/library/build.gradle b/library/build.gradle index 9dca35c..86fb526 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,21 +1,25 @@ apply plugin: 'com.android.library' -dependencies { - compile 'com.android.support:support-v4:21.0.3' -} android { - compileSdkVersion 21 - buildToolsVersion '21.1.2' + compileSdkVersion 27 + buildToolsVersion '27.0.3' defaultConfig { - minSdkVersion 8 - targetSdkVersion 21 - versionCode 2 - versionName '1.1' + minSdkVersion 14 + targetSdkVersion 27 + versionCode 3 + versionName '1.2' } -} + //库项目不生成 BuildConfig + libraryVariants.all { + it.generateBuildConfig.enabled = false + } +} +dependencies { + implementation 'com.android.support:support-v4:27.1.0' +} diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml index fcc4bde..d91cb3b 100644 --- a/library/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -3,4 +3,26 @@ package="com.loveplusplus.update"> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/java/com/loveplusplus/update/ApkUtils.java b/library/src/main/java/com/loveplusplus/update/ApkUtils.java new file mode 100644 index 0000000..79891fb --- /dev/null +++ b/library/src/main/java/com/loveplusplus/update/ApkUtils.java @@ -0,0 +1,58 @@ +package com.loveplusplus.update; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.support.v4.content.FileProvider; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +/** + * @author feicien (ithcheng@gmail.com) + * @since 2018-04-07 16:49 + */ +public class ApkUtils { + + + public static void installAPk(Context context, File apkFile) { + + Intent installAPKIntent = getApkInStallIntent(context, apkFile); + context.startActivity(installAPKIntent); + + } + + + public static Intent getApkInStallIntent(Context context, File apkFile) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".update.provider", apkFile); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(uri, "application/vnd.android.package-archive"); + } else { + Uri uri = getApkUri(apkFile); + intent.setDataAndType(uri, "application/vnd.android.package-archive"); + } + return intent; + } + + + private static Uri getApkUri(File apkFile) { + Log.d(Constants.TAG, apkFile.toString()); + //如果没有设置 SDCard 写权限,或者没有 SDCard,apk 文件保存在内存中,需要授予权限才能安装 + try { + String[] command = {"chmod", "777", apkFile.toString()}; + ProcessBuilder builder = new ProcessBuilder(command); + builder.start(); + } catch (IOException ignored) { + } + Uri uri = Uri.fromFile(apkFile); + Log.d(Constants.TAG, uri.toString()); + return uri; + } + +} diff --git a/library/src/main/java/com/loveplusplus/update/AppUtils.java b/library/src/main/java/com/loveplusplus/update/AppUtils.java new file mode 100644 index 0000000..383c6f5 --- /dev/null +++ b/library/src/main/java/com/loveplusplus/update/AppUtils.java @@ -0,0 +1,33 @@ +package com.loveplusplus.update; + +import android.content.Context; +import android.content.pm.PackageManager; + +/** + * @author feicien (ithcheng@gmail.com) + * @since 2016-07-05 17:41 + */ + +public class AppUtils { + + public static int getVersionCode(Context mContext) { + if (mContext != null) { + try { + return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionCode; + } catch (PackageManager.NameNotFoundException ignored) { + } + } + return 0; + } + + public static String getVersionName(Context mContext) { + if (mContext != null) { + try { + return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName; + } catch (PackageManager.NameNotFoundException ignored) { + } + } + + return ""; + } +} diff --git a/library/src/main/java/com/loveplusplus/update/CheckUpdateTask.java b/library/src/main/java/com/loveplusplus/update/CheckUpdateTask.java new file mode 100644 index 0000000..4fde8f4 --- /dev/null +++ b/library/src/main/java/com/loveplusplus/update/CheckUpdateTask.java @@ -0,0 +1,94 @@ +package com.loveplusplus.update; + +import android.app.ProgressDialog; +import android.content.Context; +import android.os.AsyncTask; +import android.text.TextUtils; +import android.util.Log; +import android.widget.Toast; + +import org.json.JSONException; +import org.json.JSONObject; + + +/** + * @author feicien (ithcheng@gmail.com) + * @since 2016-07-05 19:21 + */ +class CheckUpdateTask extends AsyncTask { + + private ProgressDialog dialog; + private Context mContext; + private int mType; + private boolean mShowProgressDialog; + private static final String url = Constants.UPDATE_URL; + + CheckUpdateTask(Context context, int type, boolean showProgressDialog) { + + this.mContext = context; + this.mType = type; + this.mShowProgressDialog = showProgressDialog; + + } + + + protected void onPreExecute() { + if (mShowProgressDialog) { + dialog = new ProgressDialog(mContext); + dialog.setMessage(mContext.getString(R.string.android_auto_update_dialog_checking)); + dialog.show(); + } + } + + + @Override + protected void onPostExecute(String result) { + + if (dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } + + if (!TextUtils.isEmpty(result)) { + parseJson(result); + } + } + + private void parseJson(String result) { + try { + + JSONObject obj = new JSONObject(result); + String updateMessage = obj.getString(Constants.APK_UPDATE_CONTENT); + String apkUrl = obj.getString(Constants.APK_DOWNLOAD_URL); + int apkCode = obj.getInt(Constants.APK_VERSION_CODE); + + int versionCode = AppUtils.getVersionCode(mContext); + + if (apkCode > versionCode) { + if (mType == Constants.TYPE_NOTIFICATION) { + new NotificationHelper(mContext).showNotification(updateMessage, apkUrl); + } else if (mType == Constants.TYPE_DIALOG) { + showDialog(mContext, updateMessage, apkUrl); + } + } else if (mShowProgressDialog) { + Toast.makeText(mContext, mContext.getString(R.string.android_auto_update_toast_no_new_update), Toast.LENGTH_SHORT).show(); + } + + } catch (JSONException e) { + Log.e(Constants.TAG, "parse json error"); + } + } + + + /** + * Show dialog + */ + private void showDialog(Context context, String content, String apkUrl) { + UpdateDialog.show(context, content, apkUrl); + } + + + @Override + protected String doInBackground(Void... args) { + return HttpUtils.get(url); + } +} diff --git a/library/src/main/java/com/loveplusplus/update/Constants.java b/library/src/main/java/com/loveplusplus/update/Constants.java index ddc7d0d..dc29302 100644 --- a/library/src/main/java/com/loveplusplus/update/Constants.java +++ b/library/src/main/java/com/loveplusplus/update/Constants.java @@ -1,13 +1,20 @@ package com.loveplusplus.update; -public class Constants { +class Constants { -// protected static final String APP_UPDATE_SERVER_URL = "http://192.168.205.33:8080/Hello/api/update"; - - // json {"url":"http://192.168.205.33:8080/Hello/medtime_v3.0.1_Other_20150116.apk","versionCode":2,"updateMessage":"版本更新信息"} - //我这里服务器返回的json数据是这样的,可以根据实际情况修改下面参数的名称 - public static final String APK_DOWNLOAD_URL = "url"; - public static final String APK_UPDATE_CONTENT = "updateMessage"; - public static final String APK_VERSION_CODE = "versionCode"; + // json {"url":"http://192.168.205.33:8080/Hello/app_v3.0.1_Other_20150116.apk","versionCode":2,"updateMessage":"版本更新信息"} + + static final String APK_DOWNLOAD_URL = "url"; + static final String APK_UPDATE_CONTENT = "updateMessage"; + static final String APK_VERSION_CODE = "versionCode"; + + + static final int TYPE_NOTIFICATION = 2; + + static final int TYPE_DIALOG = 1; + + static final String TAG = "UpdateChecker"; + + static final String UPDATE_URL = "https://raw.githubusercontent.com/feicien/android-auto-update/develop/extras/update.json"; } diff --git a/library/src/main/java/com/loveplusplus/update/DownloadService.java b/library/src/main/java/com/loveplusplus/update/DownloadService.java index f6a30ad..4eb7254 100644 --- a/library/src/main/java/com/loveplusplus/update/DownloadService.java +++ b/library/src/main/java/com/loveplusplus/update/DownloadService.java @@ -1,5 +1,9 @@ package com.loveplusplus.update; +import android.app.IntentService; +import android.content.Intent; +import android.util.Log; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -7,127 +11,86 @@ import java.net.HttpURLConnection; import java.net.URL; -import android.app.IntentService; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationCompat.Builder; -import android.util.Log; - public class DownloadService extends IntentService { - // 10-10 19:14:32.618: D/DownloadService(1926): 测试缓存:41234 32kb - // 10-10 19:16:10.892: D/DownloadService(2069): 测试缓存:41170 1kb - // 10-10 19:18:21.352: D/DownloadService(2253): 测试缓存:39899 10kb - private static final int BUFFER_SIZE = 10 * 1024; // 8k ~ 32K - private static final String TAG = "DownloadService"; - private NotificationManager mNotifyManager; - private Builder mBuilder; - - public DownloadService() { - super("DownloadService"); - } - - @Override - protected void onHandleIntent(Intent intent) { - - mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mBuilder = new NotificationCompat.Builder(this); - - String appName=getString(getApplicationInfo().labelRes); - int icon=getApplicationInfo().icon; - - mBuilder.setContentTitle(appName).setSmallIcon(icon); - String urlStr = intent.getStringExtra(Constants.APK_DOWNLOAD_URL); - InputStream in=null; - FileOutputStream out = null; - try { - URL url = new URL(urlStr); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - - urlConnection.setRequestMethod("GET"); - urlConnection.setDoOutput(false); - urlConnection.setConnectTimeout(10 * 1000); - urlConnection.setReadTimeout(10 * 1000); - urlConnection.setRequestProperty("Connection", "Keep-Alive"); - urlConnection.setRequestProperty("Charset", "UTF-8"); - urlConnection.setRequestProperty("Accept-Encoding", "gzip, deflate"); - - urlConnection.connect(); - long bytetotal = urlConnection.getContentLength(); - long bytesum = 0; - int byteread = 0; - in = urlConnection.getInputStream(); - File dir = StorageUtils.getCacheDirectory(this); - String apkName=urlStr.substring(urlStr.lastIndexOf("/")+1, urlStr.length()); - File apkFile = new File(dir, apkName); - out = new FileOutputStream(apkFile); - byte[] buffer = new byte[BUFFER_SIZE]; - - int oldProgress = 0; - - while ((byteread = in.read(buffer)) != -1) { - bytesum += byteread; - out.write(buffer, 0, byteread); - - int progress = (int) (bytesum * 100L / bytetotal); - // 如果进度与之前进度相等,则不更新,如果更新太频繁,否则会造成界面卡顿 - if (progress != oldProgress) { - updateProgress(progress); - } - oldProgress = progress; - } - // 下载完成 - mBuilder.setContentText(getString(R.string.download_success)).setProgress(0, 0, false); - - Intent installAPKIntent = new Intent(Intent.ACTION_VIEW); - //如果没有设置SDCard写权限,或者没有sdcard,apk文件保存在内存中,需要授予权限才能安装 - String[] command = {"chmod","777",apkFile.toString()}; - ProcessBuilder builder = new ProcessBuilder(command); - builder.start(); - - installAPKIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); - //installAPKIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - //installAPKIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - //installAPKIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, installAPKIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - mBuilder.setContentIntent(pendingIntent); - Notification noti = mBuilder.build(); - noti.flags = android.app.Notification.FLAG_AUTO_CANCEL; - mNotifyManager.notify(0, noti); - - } catch (Exception e) { - Log.e(TAG, "download apk file error", e); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if(in!=null){ - try { - in.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - private void updateProgress(int progress) { - //"正在下载:" + progress + "%" - mBuilder.setContentText(this.getString(R.string.download_progress, progress)).setProgress(100, progress, false); - //setContentInent如果不设置在4.0+上没有问题,在4.0以下会报异常 - PendingIntent pendingintent = PendingIntent.getActivity(this, 0, new Intent(), PendingIntent.FLAG_CANCEL_CURRENT); - mBuilder.setContentIntent(pendingintent); - mNotifyManager.notify(0, mBuilder.build()); - } + // 10-10 19:14:32.618: D/DownloadService(1926): 测试缓存:41234 32kb + // 10-10 19:16:10.892: D/DownloadService(2069): 测试缓存:41170 1kb + // 10-10 19:18:21.352: D/DownloadService(2253): 测试缓存:39899 10kb + private static final int BUFFER_SIZE = 10 * 1024; // 8k ~ 32K + private static final String TAG = "DownloadService"; + + + public DownloadService() { + super("DownloadService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + + NotificationHelper notificationHelper = new NotificationHelper(this); + String urlStr = intent.getStringExtra(Constants.APK_DOWNLOAD_URL); + InputStream in = null; + FileOutputStream out = null; + try { + URL url = new URL(urlStr); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod("GET"); + urlConnection.setDoOutput(false); + urlConnection.setConnectTimeout(10 * 1000); + urlConnection.setReadTimeout(10 * 1000); + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Charset", "UTF-8"); + urlConnection.setRequestProperty("Accept-Encoding", "gzip, deflate"); + + urlConnection.connect(); + long bytetotal = urlConnection.getContentLength(); + long bytesum = 0; + int byteread = 0; + in = urlConnection.getInputStream(); + File dir = StorageUtils.getCacheDirectory(this); + String apkName = urlStr.substring(urlStr.lastIndexOf("/") + 1, urlStr.length()); + File apkFile = new File(dir, apkName); + out = new FileOutputStream(apkFile); + byte[] buffer = new byte[BUFFER_SIZE]; + + int oldProgress = 0; + + while ((byteread = in.read(buffer)) != -1) { + bytesum += byteread; + out.write(buffer, 0, byteread); + + int progress = (int) (bytesum * 100L / bytetotal); + // 如果进度与之前进度相等,则不更新,如果更新太频繁,否则会造成界面卡顿 + if (progress != oldProgress) { + notificationHelper.updateProgress(progress); + } + oldProgress = progress; + } + // 下载完成 + + ApkUtils.installAPk(this, apkFile); + + notificationHelper.cancel(); + + } catch (Exception e) { + Log.e(TAG, "download apk file error:" + e.getMessage()); + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException ignored) { + + } + } + if (in != null) { + try { + in.close(); + } catch (IOException ignored) { + + } + } + } + } + } diff --git a/library/src/main/java/com/loveplusplus/update/HttpUtils.java b/library/src/main/java/com/loveplusplus/update/HttpUtils.java new file mode 100644 index 0000000..6c04380 --- /dev/null +++ b/library/src/main/java/com/loveplusplus/update/HttpUtils.java @@ -0,0 +1,61 @@ +package com.loveplusplus.update; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + + +/** + * @author feicien (ithcheng@gmail.com) + * @since 2016-07-05 19:25 + */ +public class HttpUtils { + + + public static String get(String urlStr) { + HttpURLConnection uRLConnection = null; + InputStream is = null; + BufferedReader buffer = null; + String result = null; + try { + URL url = new URL(urlStr); + uRLConnection = (HttpURLConnection) url.openConnection(); + uRLConnection.setRequestMethod("GET"); + + is = uRLConnection.getInputStream(); + buffer = new BufferedReader(new InputStreamReader(is)); + StringBuilder strBuilder = new StringBuilder(); + String line; + while ((line = buffer.readLine()) != null) { + strBuilder.append(line); + } + result = strBuilder.toString(); + } catch (Exception e) { + Log.e(Constants.TAG, "http error"); + } finally { + if (buffer != null) { + try { + buffer.close(); + } catch (IOException ignored) { + + } + } + if (is != null) { + try { + is.close(); + } catch (IOException ignored) { + + } + } + if (uRLConnection != null) { + uRLConnection.disconnect(); + } + } + return result; + } +} diff --git a/library/src/main/java/com/loveplusplus/update/NotificationHelper.java b/library/src/main/java/com/loveplusplus/update/NotificationHelper.java new file mode 100644 index 0000000..a39ce35 --- /dev/null +++ b/library/src/main/java/com/loveplusplus/update/NotificationHelper.java @@ -0,0 +1,111 @@ +package com.loveplusplus.update; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Build; +import android.support.v4.app.NotificationCompat; + +/** + * @author feicien (ithcheng@gmail.com) + * @since 2018-04-07 16:49 + */ +public class NotificationHelper extends ContextWrapper { + + private NotificationManager manager; + + private static String CHANNEL_ID = "dxy_app_update"; + + private static final int NOTIFICATION_ID = 0; + + public NotificationHelper(Context base) { + super(base); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + + NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, "应用更新", NotificationManager.IMPORTANCE_LOW); + mChannel.setDescription("应用有新版本"); + mChannel.enableLights(true); //是否在桌面icon右上角展示小红点 + getManager().createNotificationChannel(mChannel); + } + } + + /** + * Show Notification + */ + public void showNotification(String content, String apkUrl) { + + Intent myIntent = new Intent(this, DownloadService.class); + myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + myIntent.putExtra(Constants.APK_DOWNLOAD_URL, apkUrl); + PendingIntent pendingIntent = PendingIntent.getService(this, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + NotificationCompat.Builder builder = getNofity(content) + .setContentIntent(pendingIntent); + + getManager().notify(NOTIFICATION_ID, builder.build()); + } + + + public void updateProgress(int progress) { + + + String text = this.getString(R.string.android_auto_update_download_progress, progress); + + PendingIntent pendingintent = PendingIntent.getActivity(this, 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT); + + + NotificationCompat.Builder builder = getNofity(text) + .setProgress(100, progress, false) + .setContentIntent(pendingintent); + + getManager().notify(NOTIFICATION_ID, builder.build()); + } + + private NotificationCompat.Builder getNofity(String text) { + return new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID) + .setTicker(getString(R.string.android_auto_update_notify_ticker)) + .setContentTitle("应用更新") + .setContentText(text) + .setSmallIcon(getSmallIcon()) + .setLargeIcon(getLargeIcon()) + .setAutoCancel(true) + .setPriority(NotificationCompat.PRIORITY_LOW); + + } + + public void cancel() { + getManager().cancel(NOTIFICATION_ID); + } + + + private int getSmallIcon() { + //设置 nofication 的图标 直接读取小米推送配置的图标 + int icon = getResources().getIdentifier("mipush_small_notification", "drawable", getPackageName()); + if (icon == 0) { + icon = getApplicationInfo().icon; + } + + return icon; + } + + private Bitmap getLargeIcon() { + int bigIcon = getResources().getIdentifier("mipush_notification", "drawable", getPackageName()); + if (bigIcon != 0) { + return BitmapFactory.decodeResource(getResources(), bigIcon); + } + return null; + } + + private NotificationManager getManager() { + if (manager == null) { + manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + } + return manager; + } +} diff --git a/library/src/main/java/com/loveplusplus/update/StorageUtils.java b/library/src/main/java/com/loveplusplus/update/StorageUtils.java index 9200f26..19e877b 100644 --- a/library/src/main/java/com/loveplusplus/update/StorageUtils.java +++ b/library/src/main/java/com/loveplusplus/update/StorageUtils.java @@ -1,88 +1,21 @@ -/******************************************************************************* - * Copyright 2011-2013 Sergey Tarasevich - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *******************************************************************************/ package com.loveplusplus.update; -import static android.os.Environment.MEDIA_MOUNTED; - -import java.io.File; -import java.io.IOException; - import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Environment; import android.util.Log; -/** - * Provides application storage paths - * - * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) - * @since 1.0.0 - */ -public final class StorageUtils { - - private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE"; - //private static final String INDIVIDUAL_DIR_NAME = "uil-images"; - private static final String TAG = "StorageUtils"; - - private StorageUtils() { - } - - /** - * Returns application cache directory. Cache directory will be created on SD card - * ("/Android/data/[app_package_name]/cache") if card is mounted and app has appropriate permission. Else - - * Android defines cache directory on device's file system. - * - * @param context Application context - * @return Cache {@link File directory} - */ - public static File getCacheDirectory(Context context) { - File appCacheDir = null; - if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) { - appCacheDir = getExternalCacheDir(context); - } - if (appCacheDir == null) { - appCacheDir = context.getCacheDir(); - } - if (appCacheDir == null) { - Log.w(TAG,"Can't define system cache directory! The app should be re-installed."); - } - return appCacheDir; - } +import java.io.File; - +final class StorageUtils { - private static File getExternalCacheDir(Context context) { - File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"); - File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache"); - if (!appCacheDir.exists()) { - if (!appCacheDir.mkdirs()) { - Log.w(TAG,"Unable to create external cache directory"); - return null; - } - try { - new File(appCacheDir, ".nomedia").createNewFile(); - } catch (IOException e) { - Log.i(TAG,"Can't create \".nomedia\" file in application external cache directory"); - } - } - return appCacheDir; - } + /** + * 获取应用的缓存目录 + */ + public static File getCacheDirectory(Context context) { + File appCacheDir = context.getCacheDir(); + if (appCacheDir == null) { + Log.w("StorageUtils", "Can't define system cache directory! The app should be re-installed."); + } + return appCacheDir; + } - private static boolean hasExternalStoragePermission(Context context) { - int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION); - return perm == PackageManager.PERMISSION_GRANTED; - } } diff --git a/library/src/main/java/com/loveplusplus/update/UpdateApkFileProvider.java b/library/src/main/java/com/loveplusplus/update/UpdateApkFileProvider.java new file mode 100644 index 0000000..c772e55 --- /dev/null +++ b/library/src/main/java/com/loveplusplus/update/UpdateApkFileProvider.java @@ -0,0 +1,11 @@ +package com.loveplusplus.update; + +import android.support.v4.content.FileProvider; + +/** + * @author feicien (ithcheng@gmail.com) + * @since 2017-10-19 19:11 + */ + +public class UpdateApkFileProvider extends FileProvider { +} diff --git a/library/src/main/java/com/loveplusplus/update/UpdateChecker.java b/library/src/main/java/com/loveplusplus/update/UpdateChecker.java index 80a2bb4..209b8bd 100644 --- a/library/src/main/java/com/loveplusplus/update/UpdateChecker.java +++ b/library/src/main/java/com/loveplusplus/update/UpdateChecker.java @@ -1,268 +1,28 @@ package com.loveplusplus.update; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.zip.GZIPInputStream; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.app.Activity; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Bundle; -import android.os.Looper; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.app.NotificationCompat; import android.util.Log; -public class UpdateChecker extends Fragment { - - //private static final String NOTIFICATION_ICON_RES_ID_KEY = "resId"; - private static final String NOTICE_TYPE_KEY = "type"; - private static final String APP_UPDATE_SERVER_URL = "app_update_server_url"; - //private static final String SUCCESSFUL_CHECKS_REQUIRED_KEY = "nChecks"; - private static final int NOTICE_NOTIFICATION = 2; - private static final int NOTICE_DIALOG = 1; - private static final String TAG = "UpdateChecker"; - - private FragmentActivity mContext; - private Thread mThread; - private int mTypeOfNotice; - - /** - * Show a Dialog if an update is available for download. Callable in a - * FragmentActivity. Number of checks after the dialog will be shown: - * default, 5 - * - * @param fragmentActivity - * Required. - */ - public static void checkForDialog(FragmentActivity fragmentActivity,String url) { - FragmentTransaction content = fragmentActivity.getSupportFragmentManager().beginTransaction(); - UpdateChecker updateChecker = new UpdateChecker(); - Bundle args = new Bundle(); - args.putInt(NOTICE_TYPE_KEY, NOTICE_DIALOG); - args.putString(APP_UPDATE_SERVER_URL,url); - //args.putInt(SUCCESSFUL_CHECKS_REQUIRED_KEY, 5); - updateChecker.setArguments(args); - content.add(updateChecker, null).commit(); - } - - - - /** - * Show a Notification if an update is available for download. Callable in a - * FragmentActivity Specify the number of checks after the notification will - * be shown. - * - * @param fragmentActivity - * Required. - */ - public static void checkForNotification(FragmentActivity fragmentActivity,String url) { - FragmentTransaction content = fragmentActivity.getSupportFragmentManager().beginTransaction(); - UpdateChecker updateChecker = new UpdateChecker(); - Bundle args = new Bundle(); - args.putInt(NOTICE_TYPE_KEY, NOTICE_NOTIFICATION); - args.putString(APP_UPDATE_SERVER_URL,url); - //args.putInt(NOTIFICATION_ICON_RES_ID_KEY, notificationIconResId); - //args.putInt(SUCCESSFUL_CHECKS_REQUIRED_KEY, 5); - updateChecker.setArguments(args); - content.add(updateChecker, null).commit(); - } - - - /** - * This class is a Fragment. Check for the method you have chosen. - */ - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mContext = (FragmentActivity) activity; - Bundle args = getArguments(); - mTypeOfNotice = args.getInt(NOTICE_TYPE_KEY); - String url = args.getString(APP_UPDATE_SERVER_URL); - //mSuccessfulChecksRequired = args.getInt(SUCCESSFUL_CHECKS_REQUIRED_KEY); - //mNotificationIconResId = args.getInt(NOTIFICATION_ICON_RES_ID_KEY); - checkForUpdates(url); - } - - /** - * Heart of the library. Check if an update is available for download - * parsing the desktop Play Store page of the app - */ - private void checkForUpdates(final String url) { - mThread = new Thread() { - @Override - public void run() { - //if (isNetworkAvailable(mContext)) { - - String json = sendPost(url); - if(json!=null){ - parseJson(json); - }else{ - Log.e(TAG, "can't get app update json"); - } - //} - } - - }; - mThread.start(); - } - - protected String sendPost(String urlStr) { - HttpURLConnection uRLConnection = null; - InputStream is = null; - BufferedReader buffer = null; - String result = null; - try { - URL url = new URL(urlStr); - uRLConnection = (HttpURLConnection) url.openConnection(); - uRLConnection.setDoInput(true); - uRLConnection.setDoOutput(true); - uRLConnection.setRequestMethod("POST"); - uRLConnection.setUseCaches(false); - uRLConnection.setConnectTimeout(10 * 1000); - uRLConnection.setReadTimeout(10 * 1000); - uRLConnection.setInstanceFollowRedirects(false); - uRLConnection.setRequestProperty("Connection", "Keep-Alive"); - uRLConnection.setRequestProperty("Charset", "UTF-8"); - uRLConnection.setRequestProperty("Accept-Encoding", "gzip, deflate"); - uRLConnection.setRequestProperty("Content-Type", "application/json"); - - uRLConnection.connect(); - - - is = uRLConnection.getInputStream(); - - String content_encode = uRLConnection.getContentEncoding(); - - if (null != content_encode && !"".equals(content_encode) && content_encode.equals("gzip")) { - is = new GZIPInputStream(is); - } - - buffer = new BufferedReader(new InputStreamReader(is)); - StringBuilder strBuilder = new StringBuilder(); - String line; - while ((line = buffer.readLine()) != null) { - strBuilder.append(line); - } - result = strBuilder.toString(); - } catch (Exception e) { - Log.e(TAG, "http post error", e); - } finally { - if(buffer!=null){ - try { - buffer.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if(is!=null){ - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if(uRLConnection!=null){ - uRLConnection.disconnect(); - } - } - return result; - } - - - private void parseJson(String json) { - mThread.interrupt(); - Looper.prepare(); - try { - - JSONObject obj = new JSONObject(json); - String updateMessage = obj.getString(Constants.APK_UPDATE_CONTENT); - String apkUrl = obj.getString(Constants.APK_DOWNLOAD_URL); - int apkCode = obj.getInt(Constants.APK_VERSION_CODE); - - int versionCode = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionCode; - - if (apkCode > versionCode) { - if (mTypeOfNotice == NOTICE_NOTIFICATION) { - showNotification(updateMessage,apkUrl); - } else if (mTypeOfNotice == NOTICE_DIALOG) { - showDialog(updateMessage,apkUrl); - } - } else { - //Toast.makeText(mContext, mContext.getString(R.string.app_no_new_update), Toast.LENGTH_SHORT).show(); - } - - } catch (PackageManager.NameNotFoundException ignored) { - } catch (JSONException e) { - Log.e(TAG, "parse json error", e); - } - } +public class UpdateChecker { - /** - * Show dialog - * - */ - public void showDialog(String content,String apkUrl) { - UpdateDialog d = new UpdateDialog(); - Bundle args = new Bundle(); - args.putString(Constants.APK_UPDATE_CONTENT, content); - args.putString(Constants.APK_DOWNLOAD_URL, apkUrl); - d.setArguments(args); - d.show(mContext.getSupportFragmentManager(), null); - } - /** - * Show Notification - * - */ - public void showNotification(String content,String apkUrl) { - android.app.Notification noti; - Intent myIntent = new Intent(mContext, DownloadService.class); - myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - myIntent.putExtra(Constants.APK_DOWNLOAD_URL, apkUrl); - PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT); + public static void checkForDialog(Context context) { + if (context != null) { + new CheckUpdateTask(context, Constants.TYPE_DIALOG, true).execute(); + } else { + Log.e(Constants.TAG, "The arg context is null"); + } + } - int smallIcon = mContext.getApplicationInfo().icon; - noti = new NotificationCompat.Builder(mContext).setTicker(getString(R.string.newUpdateAvailable)) - .setContentTitle(getString(R.string.newUpdateAvailable)).setContentText(content).setSmallIcon(smallIcon) - .setContentIntent(pendingIntent).build(); - noti.flags = android.app.Notification.FLAG_AUTO_CANCEL; - NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.notify(0, noti); - } + public static void checkForNotification(Context context) { + if (context != null) { + new CheckUpdateTask(context, Constants.TYPE_NOTIFICATION, false).execute(); + } else { + Log.e(Constants.TAG, "The arg context is null"); + } - + } - /** - * Check if a network available - */ - public static boolean isNetworkAvailable(Context context) { - boolean connected = false; - ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (cm != null) { - NetworkInfo ni = cm.getActiveNetworkInfo(); - if (ni != null) { - connected = ni.isConnected(); - } - } - return connected; - } - } diff --git a/library/src/main/java/com/loveplusplus/update/UpdateDialog.java b/library/src/main/java/com/loveplusplus/update/UpdateDialog.java index 4740214..84b4640 100644 --- a/library/src/main/java/com/loveplusplus/update/UpdateDialog.java +++ b/library/src/main/java/com/loveplusplus/update/UpdateDialog.java @@ -1,42 +1,41 @@ package com.loveplusplus.update; +import android.app.Activity; import android.app.AlertDialog; -import android.app.Dialog; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -public class UpdateDialog extends DialogFragment { +class UpdateDialog { - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - // Use the Builder class for convenient dialog construction - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle(R.string.newUpdateAvailable); - builder.setMessage(getArguments().getString(Constants.APK_UPDATE_CONTENT)) - .setPositiveButton(R.string.dialogPositiveButton, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - // FIRE ZE MISSILES! - goToDownload(); - dismiss(); - } - }) - .setNegativeButton(R.string.dialogNegativeButton, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - // User cancelled the dialog - dismiss(); - } - }); - // Create the AlertDialog object and return it - return builder.create(); + static void show(final Context context, String content, final String downloadUrl) { + if (isContextValid(context)) { + new AlertDialog.Builder(context) + .setTitle(R.string.android_auto_update_dialog_title) + .setMessage(content) + .setPositiveButton(R.string.android_auto_update_dialog_btn_download, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + goToDownload(context, downloadUrl); + } + }) + .setNegativeButton(R.string.android_auto_update_dialog_btn_cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + } + }) + .setCancelable(false) + .show(); + } + } + + private static boolean isContextValid(Context context) { + return context instanceof Activity && !((Activity) context).isFinishing(); } - private void goToDownload() { - Intent intent=new Intent(getActivity().getApplicationContext(),DownloadService.class); - intent.putExtra(Constants.APK_DOWNLOAD_URL, getArguments().getString(Constants.APK_DOWNLOAD_URL)); - getActivity().startService(intent); + private static void goToDownload(Context context, String downloadUrl) { + Intent intent = new Intent(context.getApplicationContext(), DownloadService.class); + intent.putExtra(Constants.APK_DOWNLOAD_URL, downloadUrl); + context.startService(intent); } } diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml index 21194f4..fcf571e 100644 --- a/library/src/main/res/values/strings.xml +++ b/library/src/main/res/values/strings.xml @@ -1,12 +1,19 @@ - 发现新版本 - 立即下载 - 以后再说 - 没有发现新版本 - 正在下载:%1$d%% - 下载完成,点击安装 - - + 发现新版本 + + 发现新版本,点击进行升级 + 发现新版本,点击进行升级 + + 立即下载 + 以后再说 + + 已经是最新版本 + + 正在下载:%1$d%% + + 正在检查版本 + + \ No newline at end of file diff --git a/library/src/main/res/xml/update_apk_paths.xml b/library/src/main/res/xml/update_apk_paths.xml new file mode 100644 index 0000000..7145f3a --- /dev/null +++ b/library/src/main/res/xml/update_apk_paths.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/sample/build.gradle b/sample/build.gradle index 1623408..e218acb 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,27 +1,60 @@ apply plugin: 'com.android.application' + +dependencies { + implementation project(':library') + implementation "com.android.support:appcompat-v7:27.1.0" +} + + android { - compileSdkVersion 21 - buildToolsVersion "21.1.2" + compileSdkVersion 27 + buildToolsVersion '27.0.3' defaultConfig { applicationId "com.loveplusplus.update.sample" - minSdkVersion 8 - targetSdkVersion 21 - versionCode 2 - versionName "1.1" + minSdkVersion 14 + targetSdkVersion 27 + versionCode 3 + versionName "1.2" } - buildTypes { + + signingConfigs { + debug { + storeFile file("../extras/demo.jks") + storePassword "android" + keyAlias "android" + keyPassword "android" + } + release { + storeFile file("../extras/demo.jks") + storePassword "android" + keyAlias "android" + keyPassword "android" + } + } + + buildTypes { + debug { + debuggable true + signingConfig signingConfigs.debug minifyEnabled false } + + release { + signingConfig signingConfigs.release + minifyEnabled true + shrinkResources false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' + zipAlignEnabled true + } } + + lintOptions { abortOnError false } } -dependencies { - compile project(':library') - compile "com.android.support:appcompat-v7:21.0.3" -} + diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 810a179..776a418 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -3,11 +3,6 @@ package="com.loveplusplus.update.sample"> - - - - - - + \ No newline at end of file diff --git a/sample/src/main/java/com/loveplusplus/update/sample/MainActivity.java b/sample/src/main/java/com/loveplusplus/update/sample/MainActivity.java index 498e89e..fdc0a38 100644 --- a/sample/src/main/java/com/loveplusplus/update/sample/MainActivity.java +++ b/sample/src/main/java/com/loveplusplus/update/sample/MainActivity.java @@ -1,41 +1,45 @@ package com.loveplusplus.update.sample; import android.os.Bundle; -import android.support.v4.app.FragmentActivity; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; +import android.widget.TextView; +import com.loveplusplus.update.AppUtils; import com.loveplusplus.update.UpdateChecker; -public class MainActivity extends ActionBarActivity { +public class MainActivity extends AppCompatActivity { - protected static final String APP_UPDATE_SERVER_URL = "http://192.168.205.33:8080/Hello/api/update"; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); - Button btn1 = (Button) findViewById(R.id.button1); - Button btn2 = (Button) findViewById(R.id.button2); + Button btn1 = (Button) findViewById(R.id.button1); + Button btn2 = (Button) findViewById(R.id.button2); - btn1.setOnClickListener(new OnClickListener() { + btn1.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - UpdateChecker.checkForDialog(MainActivity.this,APP_UPDATE_SERVER_URL); - } - }); - btn2.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + UpdateChecker.checkForDialog(MainActivity.this); + } + }); + btn2.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - UpdateChecker.checkForNotification(MainActivity.this,APP_UPDATE_SERVER_URL); - } - }); + @Override + public void onClick(View v) { + UpdateChecker.checkForNotification(MainActivity.this); + } + }); - } + + TextView textView = (TextView) findViewById(R.id.textView1); + + textView.setText("当前版本信息: versionName = " + AppUtils.getVersionName(this) + " versionCode = " + AppUtils.getVersionCode(this)); + } } diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 9876c6e..980b762 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,12 +1,13 @@ - + tools:context=".MainActivity">