diff --git a/README.md b/README.md index 1877009..1aa0971 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![Image](app/src/main/ic_launcher-web.png) [![Download](https://img.shields.io/badge/download-App-blue.svg)](https://raw.githubusercontent.com/jenly1314/AppUpdater/master/app/release/app-release.apk) -[![Jitpack](https://jitpack.io/v/jenly1314/AppUpdater.svg)](https://jitpack.io/#jenly1314/AppUpdater) +[![JitPack](https://jitpack.io/v/jenly1314/AppUpdater.svg)](https://jitpack.io/#jenly1314/AppUpdater) [![CI](https://travis-ci.org/jenly1314/AppUpdater.svg?branch=master)](https://travis-ci.org/jenly1314/AppUpdater) [![CircleCI](https://circleci.com/gh/jenly1314/AppUpdater.svg?style=svg)](https://circleci.com/gh/jenly1314/AppUpdater) [![API](https://img.shields.io/badge/API-15%2B-blue.svg?style=flat)](https://android-arsenal.com/api?level=15) @@ -25,7 +25,8 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 - [x] 支持下载失败,重新下载 - [x] 支持下载优先取本地缓存 - [x] 支持通知栏提示内容和过程全部可配置 -- [x] 支持Android O +- [x] 支持Android Q(10) +- [x] 支持使用OkHttpClient下载 ### [AndroidX version](https://github.com/jenly1314/AppUpdater/tree/androidx) @@ -40,7 +41,7 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 com.king.app app-updater - 1.0.5 + 1.0.6 pom @@ -48,7 +49,7 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 com.king.app app-dialog - 1.0.5 + 1.0.6 pom ``` @@ -57,25 +58,25 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 //----------AndroidX 版本 //app-updater - implementation 'com.king.app:app-updater:1.0.5-androidx' + implementation 'com.king.app:app-updater:1.0.6-androidx' //app-dialog - implementation 'com.king.app:app-dialog:1.0.5-androidx' + implementation 'com.king.app:app-dialog:1.0.6-androidx' - //----------Android 版本 + //----------Android Support 版本 //app-updater - implementation 'com.king.app:app-updater:1.0.5' + implementation 'com.king.app:app-updater:1.0.6' //app-dialog - implementation 'com.king.app:app-dialog:1.0.5' + implementation 'com.king.app:app-dialog:1.0.6' ``` ### Lvy: ```lvy //app-updater - + //app-dialog - + ``` @@ -128,6 +129,7 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 .serUrl(mUrl) .setFilename("AppUpdater.apk") .build(getContext()) + .setHttpManager(OkHttpManager.getInstance())//使用OkHttpClient实现下载,需依赖okhttp库 .start(); AppDialog.INSTANCE.dismissDialogFragment(getSupportFragmentManager()); } @@ -140,6 +142,10 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 ## 版本记录 +#### v1.0.6:2019-11-27 +* 新增OkHttpManager 如果使用了OkHttpManager则必须依赖[okhttp](https://github.com/square/okhttp) +* 优化细节 (progress,total 变更 int -> long) + #### v1.0.5:2019-9-4 * 支持取消下载 diff --git a/app-dialog/src/main/java/com/king/app/dialog/AppDialog.java b/app-dialog/src/main/java/com/king/app/dialog/AppDialog.java index b2f0e23..21e4cf0 100644 --- a/app-dialog/src/main/java/com/king/app/dialog/AppDialog.java +++ b/app-dialog/src/main/java/com/king/app/dialog/AppDialog.java @@ -234,8 +234,9 @@ public enum AppDialog { public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if(keyCode == KeyEvent.KEYCODE_BACK && isCancel){ dismissDialog(); + return true; } - return true; + return false; } }); diff --git a/app-updater/build.gradle b/app-updater/build.gradle index 952d0f8..82b52fa 100644 --- a/app-updater/build.gradle +++ b/app-updater/build.gradle @@ -36,5 +36,6 @@ dependencies { androidTestImplementation deps.test.espresso //support compileOnly deps.support.appcompat + compileOnly deps.okhttp } diff --git a/app-updater/src/main/java/com/king/app/updater/AppUpdater.java b/app-updater/src/main/java/com/king/app/updater/AppUpdater.java index ab4a5e4..323e503 100644 --- a/app-updater/src/main/java/com/king/app/updater/AppUpdater.java +++ b/app-updater/src/main/java/com/king/app/updater/AppUpdater.java @@ -13,7 +13,9 @@ import android.util.Log; import com.king.app.updater.callback.UpdateCallback; import com.king.app.updater.constant.Constants; +import com.king.app.updater.http.HttpManager; import com.king.app.updater.http.IHttpManager; +import com.king.app.updater.http.OkHttpManager; import com.king.app.updater.service.DownloadService; import com.king.app.updater.util.PermissionUtils; @@ -54,11 +56,24 @@ public class AppUpdater { mConfig.setUrl(url); } + /** + * 设置下载更新进度回调 + * @param callback + * @return + */ public AppUpdater setUpdateCallback(UpdateCallback callback){ this.mCallback = callback; return this; } + /** + * 设置一个IHttpManager + * @param httpManager AppUpdater内置提供{@link HttpManager} 和 {@link OkHttpManager}两种实现。 + * 如果不设置,将默认使用{@link HttpManager},你也可以使用{@link OkHttpManager}或自己去实现一个 + * {@link IHttpManager}。 + * 当使用{@link OkHttpManager}时,必需依赖okhttp库 + * @return + */ public AppUpdater setHttpManager(IHttpManager httpManager){ this.mHttpManager = httpManager; return this; @@ -71,10 +86,10 @@ public class AppUpdater { if(mConfig!=null && !TextUtils.isEmpty(mConfig.getUrl())){ //如果mContext是Activity,则默认会校验一次动态权限。 if(mContext instanceof Activity){ - PermissionUtils.INSTANCE.verifyReadAndWritePermissions((Activity) mContext,Constants.RE_CODE_STORAGE_PERMISSION); + PermissionUtils.verifyReadAndWritePermissions((Activity) mContext,Constants.RE_CODE_STORAGE_PERMISSION); } - if(mConfig.isShowNotification() && !PermissionUtils.INSTANCE.isNotificationEnabled(mContext)){ + if(mConfig.isShowNotification() && !PermissionUtils.isNotificationEnabled(mContext)){ Log.w(Constants.TAG,"Notification permission not enabled."); } @@ -128,7 +143,7 @@ public class AppUpdater { } /** - * AppUpdater构建器 + * AppUpdater建造者 */ public static class Builder{ @@ -150,7 +165,7 @@ public class AppUpdater { /** * 设置保存的路径 - * @param path 下载保存的文件路径 (默认SD卡/.AppUpdater目录) + * @param path 下载保存的文件路径 * @return */ public Builder setPath(String path){ @@ -251,7 +266,7 @@ public class AppUpdater { /** * 设置FileProvider的authority - * @param authority FileProvider的authority(默认兼容N,默认值context.getPackageName() + ".fileProvider") + * @param authority FileProvider的authority(默认兼容N,默认值{@link Context#getPackageName() + ".fileProvider"}) * @return */ public Builder setAuthority(String authority){ diff --git a/app-updater/src/main/java/com/king/app/updater/callback/UpdateCallback.java b/app-updater/src/main/java/com/king/app/updater/callback/UpdateCallback.java index 818a892..e077cb3 100644 --- a/app-updater/src/main/java/com/king/app/updater/callback/UpdateCallback.java +++ b/app-updater/src/main/java/com/king/app/updater/callback/UpdateCallback.java @@ -24,7 +24,7 @@ public interface UpdateCallback { * @param total * @param isChange 进度百分比是否有改变,(主要可以用来过滤无用的刷新,从而降低刷新频率) */ - void onProgress(int progress,int total,boolean isChange); + void onProgress(long progress,long total,boolean isChange); /** * 完成 diff --git a/app-updater/src/main/java/com/king/app/updater/constant/Constants.java b/app-updater/src/main/java/com/king/app/updater/constant/Constants.java index 047b66b..cd04af9 100644 --- a/app-updater/src/main/java/com/king/app/updater/constant/Constants.java +++ b/app-updater/src/main/java/com/king/app/updater/constant/Constants.java @@ -1,9 +1,5 @@ package com.king.app.updater.constant; -import android.os.Environment; - -import java.io.File; - /** * @author Jenly Jenly */ @@ -23,9 +19,12 @@ public final class Constants { public static final String KEY_RE_DOWNLOAD = "app_update_re_download"; - public static final String DEFAULT_DIR_PATH = Environment.getExternalStorageDirectory() + File.separator + ".AppUpdater"; - public static final int RE_CODE_STORAGE_PERMISSION = 0x66; public static final int NONE = -1; + + public static final String DEFAULT_FILE_PROVIDER = ".fileProvider"; + + public static final String DEFAULT_DIR = "apk"; + } diff --git a/app-updater/src/main/java/com/king/app/updater/http/HttpManager.java b/app-updater/src/main/java/com/king/app/updater/http/HttpManager.java index 7251c87..10eb5c2 100644 --- a/app-updater/src/main/java/com/king/app/updater/http/HttpManager.java +++ b/app-updater/src/main/java/com/king/app/updater/http/HttpManager.java @@ -19,6 +19,7 @@ import java.util.Map; import javax.net.ssl.HttpsURLConnection; /** + * HttpManager使用{@link HttpURLConnection}实现{@link IHttpManager} * @author Jenly Jenly */ public class HttpManager implements IHttpManager { @@ -29,12 +30,14 @@ public class HttpManager implements IHttpManager { private boolean isCancel; - private static HttpManager INSTANCE; + private static volatile HttpManager INSTANCE; public static HttpManager getInstance(){ if(INSTANCE == null){ synchronized (HttpManager.class){ - INSTANCE = new HttpManager(); + if(INSTANCE == null){ + INSTANCE = new HttpManager(); + } } } @@ -45,6 +48,9 @@ public class HttpManager implements IHttpManager { this(DEFAULT_TIME_OUT); } + /** + * HttpManager对外暴露。如果没有特殊需求,推荐使用{@link HttpManager#getInstance()} + */ public HttpManager(int timeout){ this.mTimeout = timeout; } @@ -63,7 +69,7 @@ public class HttpManager implements IHttpManager { /** * 异步下载任务 */ - private class DownloadTask extends AsyncTask { + private class DownloadTask extends AsyncTask { private String url; private String path; @@ -111,15 +117,17 @@ public class HttpManager implements IHttpManager { InputStream is = connect.getInputStream(); - int length = connect.getContentLength(); + long length = connect.getContentLength(); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - length = (int)connect.getContentLengthLong(); + length = connect.getContentLengthLong(); } - int progress = 0; + Log.d(Constants.TAG,"contentLength:" + length); - byte[] buffer = new byte[4096]; + long progress = 0; + + byte[] buffer = new byte[8192]; int len; File file = new File(path,filename); @@ -132,7 +140,9 @@ public class HttpManager implements IHttpManager { fos.write(buffer,0,len); progress += len; //更新进度 - publishProgress(progress,length); + if(length>0){ + publishProgress(progress,length); + } } fos.flush(); @@ -176,7 +186,7 @@ public class HttpManager implements IHttpManager { } @Override - protected void onProgressUpdate(Integer... values) { + protected void onProgressUpdate(Long... values) { super.onProgressUpdate(values); if(callback!=null){ if(!isCancelled()){ diff --git a/app-updater/src/main/java/com/king/app/updater/http/IHttpManager.java b/app-updater/src/main/java/com/king/app/updater/http/IHttpManager.java index 8d8e724..a186522 100644 --- a/app-updater/src/main/java/com/king/app/updater/http/IHttpManager.java +++ b/app-updater/src/main/java/com/king/app/updater/http/IHttpManager.java @@ -7,6 +7,7 @@ import java.io.Serializable; import java.util.Map; /** + * IHttpManager 默认提供{@link HttpManager} 和 {@link OkHttpManager}两种实现。 * @author Jenly Jenly */ public interface IHttpManager { @@ -39,7 +40,7 @@ public interface IHttpManager { * @param progress * @param total */ - void onProgress(int progress,int total); + void onProgress(long progress,long total); /** * 完成 diff --git a/app-updater/src/main/java/com/king/app/updater/http/OkHttpManager.java b/app-updater/src/main/java/com/king/app/updater/http/OkHttpManager.java new file mode 100644 index 0000000..5347059 --- /dev/null +++ b/app-updater/src/main/java/com/king/app/updater/http/OkHttpManager.java @@ -0,0 +1,227 @@ +package com.king.app.updater.http; + +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.king.app.updater.constant.Constants; +import com.king.app.updater.util.SSLSocketFactoryUtils; + +import org.apache.http.conn.ssl.SSLSocketFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.ConnectException; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +/** + * OkHttpManager使用{@link OkHttpClient}实现{@link IHttpManager} + * 使用OkHttpManager时必须依赖OkHttp库 + * @author Jenly + */ +public class OkHttpManager implements IHttpManager { + + private static final int DEFAULT_TIME_OUT = 20000; + + private OkHttpClient okHttpClient; + + private boolean isCancel; + + private static volatile OkHttpManager INSTANCE; + + public static OkHttpManager getInstance(){ + if(INSTANCE == null){ + synchronized (HttpManager.class){ + if(INSTANCE == null){ + INSTANCE = new OkHttpManager(); + } + } + } + + return INSTANCE; + } + + private OkHttpManager(){ + this(DEFAULT_TIME_OUT); + } + + /** + * HttpManager对外暴露。如果没有特殊需求,推荐使用{@link HttpManager#getInstance()} + * @param timeout 超时时间,单位:毫秒 + */ + public OkHttpManager(int timeout){ + this(new OkHttpClient.Builder() + .readTimeout(timeout, TimeUnit.MILLISECONDS) + .connectTimeout(timeout, TimeUnit.MILLISECONDS) + .sslSocketFactory(SSLSocketFactoryUtils.createSSLSocketFactory(),SSLSocketFactoryUtils.createTrustAllManager()) + .hostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) + .build()); + } + + /** + * HttpManager对外暴露,推荐使用{@link HttpManager#getInstance()} + * @param okHttpClient {@link OkHttpClient} + */ + public OkHttpManager(@NonNull OkHttpClient okHttpClient){ + this.okHttpClient = okHttpClient; + } + + + @Override + public void download(String url,final String path,final String filename, @Nullable Map requestProperty,final DownloadCallback callback) { + isCancel = false; + new DownloadTask(okHttpClient,url,path,filename,requestProperty,callback).execute(); + } + + @Override + public void cancel() { + isCancel = true; + } + + + /** + * 异步下载任务 + */ + private class DownloadTask extends AsyncTask { + private String url; + + private String path; + + private String filename; + + private Map requestProperty; + + private DownloadCallback callback; + + private Exception exception; + + private OkHttpClient okHttpClient; + + public DownloadTask(OkHttpClient okHttpClient,String url, String path, String filename ,@Nullable Map requestProperty, DownloadCallback callback){ + this.okHttpClient = okHttpClient; + this.url = url; + this.path = path; + this.filename = filename; + this.callback = callback; + this.requestProperty = requestProperty; + + } + + @Override + protected File doInBackground(Void... voids) { + + try{ + Request.Builder builder = new Request.Builder() + .url(url) + .addHeader("Accept-Encoding", "identity") + .get(); + + if(requestProperty!=null){ + for(Map.Entry entry : requestProperty.entrySet()){ + builder.addHeader(entry.getKey(),entry.getValue()); + } + } + + Call call = okHttpClient.newCall(builder.build()); + Response response = call.execute(); + + if(response.isSuccessful()){ + InputStream is = response.body().byteStream(); + + long length = response.body().contentLength(); + + Log.d(Constants.TAG,"contentLength:" + length); + + long progress = 0; + + byte[] buffer = new byte[8192]; + + int len; + File file = new File(path,filename); + FileOutputStream fos = new FileOutputStream(file); + while ((len = is.read(buffer)) != -1){ + if(isCancel){ + if(call != null){ + call.cancel(); + } + cancel(true); + break; + } + fos.write(buffer,0,len); + progress += len; + //更新进度 + if(length>0){ + publishProgress(progress,length); + } + } + + fos.flush(); + fos.close(); + is.close(); + + response.close(); + + return file; + + }else {//连接失败 + throw new ConnectException(String.format("responseCode = %d",response.code())); + } + + }catch (Exception e){ + this.exception = e; + e.printStackTrace(); + } + + return null; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + if(callback!=null){ + callback.onStart(url); + } + } + + @Override + protected void onPostExecute(File file) { + super.onPostExecute(file); + if(callback!=null){ + if(file!=null){ + callback.onFinish(file); + }else{ + callback.onError(exception); + } + + } + } + + @Override + protected void onProgressUpdate(Long... values) { + super.onProgressUpdate(values); + if(callback!=null){ + if(!isCancelled()){ + callback.onProgress(values[0],values[1]); + } + + } + } + + @Override + protected void onCancelled() { + super.onCancelled(); + if(callback!=null){ + callback.onCancel(); + } + } + + } +} diff --git a/app-updater/src/main/java/com/king/app/updater/service/DownloadService.java b/app-updater/src/main/java/com/king/app/updater/service/DownloadService.java index 1535c46..027c7f2 100644 --- a/app-updater/src/main/java/com/king/app/updater/service/DownloadService.java +++ b/app-updater/src/main/java/com/king/app/updater/service/DownloadService.java @@ -7,16 +7,14 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.net.Uri; import android.os.Binder; import android.os.Build; -import android.os.Environment; import android.os.IBinder; import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.v4.app.NotificationCompat; -import android.support.v4.content.FileProvider; +import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.util.Log; @@ -118,16 +116,16 @@ public class DownloadService extends Service { //如果保存路径为空则使用缓存路径 if(TextUtils.isEmpty(path)){ - path = getDiskCacheDir(getContext()); + path = getExternalFilesDir(getContext()); } File dirFile = new File(path); if(!dirFile.exists()){ - dirFile.mkdir(); + dirFile.mkdirs(); } //如果文件名为空则使用路径 if(TextUtils.isEmpty(filename)){ - filename = AppUtils.INSTANCE.getAppFullName(getContext(),url,getResources().getString(R.string.app_name)); + filename = AppUtils.getAppFullName(getContext(),url,getResources().getString(R.string.app_name)); } mFile = new File(path,filename); @@ -135,15 +133,15 @@ public class DownloadService extends Service { Integer versionCode = config.getVersionCode(); if(versionCode!=null){ try{ - if(AppUtils.INSTANCE.apkExists(getContext(),versionCode,mFile)){ + if(AppUtils.apkExists(getContext(),versionCode,mFile)){ //本地已经存在要下载的APK Log.d(Constants.TAG,"CacheFile:" + mFile); if(config.isInstallApk()){ String authority = config.getAuthority(); if(TextUtils.isEmpty(authority)){//如果为空则默认 - authority = getContext().getPackageName() + ".fileProvider"; + authority = getContext().getPackageName() + Constants.DEFAULT_FILE_PROVIDER; } - AppUtils.INSTANCE.installApk(getContext(),mFile,authority); + AppUtils.installApk(getContext(),mFile,authority); } if(callback!=null){ callback.onFinish(mFile); @@ -183,12 +181,13 @@ public class DownloadService extends Service { * @param context * @return */ - public String getDiskCacheDir(Context context) { - if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - return Constants.DEFAULT_DIR_PATH; + private String getExternalFilesDir(Context context) { + File[] files = ContextCompat.getExternalFilesDirs(context,Constants.DEFAULT_DIR); + if(files!=null && files.length > 0){ + return files[0].getAbsolutePath(); } + return context.getExternalFilesDir(Constants.DEFAULT_DIR).getAbsolutePath(); - return context.getCacheDir().getAbsolutePath(); } /** @@ -237,8 +236,8 @@ public class DownloadService extends Service { this.channelId = TextUtils.isEmpty(config.getChannelId()) ? Constants.DEFAULT_NOTIFICATION_CHANNEL_ID : config.getChannelId(); this.channelName = TextUtils.isEmpty(config.getChannelName()) ? Constants.DEFAULT_NOTIFICATION_CHANNEL_NAME : config.getChannelName(); } - if(config.getNotificationIcon()<=0){ - this.notificationIcon = AppUtils.INSTANCE.getAppIcon(getContext()); + if(config.getNotificationIcon() <=0 ){ + this.notificationIcon = AppUtils.getAppIcon(getContext()); }else{ this.notificationIcon = config.getNotificationIcon(); } @@ -247,7 +246,7 @@ public class DownloadService extends Service { this.authority = config.getAuthority(); if(TextUtils.isEmpty(config.getAuthority())){//如果为空则默认 - authority = getContext().getPackageName() + ".fileProvider"; + authority = getContext().getPackageName() + Constants.DEFAULT_FILE_PROVIDER; } this.isShowPercentage = config.isShowPercentage(); @@ -255,7 +254,6 @@ public class DownloadService extends Service { } - @Override public void onStart(String url) { Log.d(Constants.TAG,"onStart:" + url); @@ -271,7 +269,7 @@ public class DownloadService extends Service { } @Override - public void onProgress(int progress, int total) { + public void onProgress(long progress, long total) { boolean isChange = false; long curTime = System.currentTimeMillis(); @@ -289,7 +287,7 @@ public class DownloadService extends Service { content += percentage; } - showProgressNotification(notifyId, channelId, notificationIcon, getString(R.string.app_updater_progress_notification_title), content, progress, total); + showProgressNotification(notifyId, channelId, notificationIcon, getString(R.string.app_updater_progress_notification_title), content, currProgress, 100); } } @@ -306,7 +304,7 @@ public class DownloadService extends Service { isDownloading = false; showFinishNotification(notifyId,channelId,notificationIcon,getString(R.string.app_updater_finish_notification_title),getString(R.string.app_updater_finish_notification_content),file,authority); if(isInstallApk){ - AppUtils.INSTANCE.installApk(getContext(),file,authority); + AppUtils.installApk(getContext(),file,authority); } if(callback!=null){ callback.onFinish(file); @@ -412,18 +410,7 @@ public class DownloadService extends Service { cancelNotification(notifyId); NotificationCompat.Builder builder = buildNotification(channelId,icon,title,content); builder.setAutoCancel(true); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addCategory(Intent.CATEGORY_DEFAULT); - Uri uriData; - String type = "application/vnd.android.package-archive"; - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - uriData = FileProvider.getUriForFile(getContext(), authority, file); - }else{ - uriData = Uri.fromFile(file); - } - intent.setDataAndType(uriData, type); + Intent intent = AppUtils.getInstallIntent(getContext(),file,authority); PendingIntent clickIntent = PendingIntent.getActivity(getContext(), notifyId,intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(clickIntent); Notification notification = builder.build(); @@ -468,7 +455,7 @@ public class DownloadService extends Service { } /** - * + * 取消通知 * @param notifyId */ private void cancelNotification(int notifyId){ diff --git a/app-updater/src/main/java/com/king/app/updater/util/AppUtils.java b/app-updater/src/main/java/com/king/app/updater/util/AppUtils.java index 5411ccf..8fa4be8 100644 --- a/app-updater/src/main/java/com/king/app/updater/util/AppUtils.java +++ b/app-updater/src/main/java/com/king/app/updater/util/AppUtils.java @@ -1,30 +1,36 @@ package com.king.app.updater.util; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.res.AssetFileDescriptor; import android.net.Uri; import android.os.Build; import android.support.v4.content.FileProvider; import android.text.TextUtils; import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; /** * @author Jenly Jenly */ -public enum AppUtils { +public final class AppUtils { - INSTANCE; + private AppUtils(){ + throw new AssertionError(); + } /** * 通过url获取App的全名称 * @param context * @return AppName.apk */ - public String getAppFullName(Context context,String url,String defaultName){ + public static String getAppFullName(Context context,String url,String defaultName){ if(url.endsWith(".apk")){ return url.substring(url.lastIndexOf("/") + 1); } @@ -44,7 +50,7 @@ public enum AppUtils { * @return * @throws PackageManager.NameNotFoundException */ - public PackageInfo getPackageInfo(Context context) throws PackageManager.NameNotFoundException { + public static PackageInfo getPackageInfo(Context context) throws PackageManager.NameNotFoundException { PackageManager packageManager = context.getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo( context.getPackageName(), 0); return packageInfo; @@ -66,7 +72,7 @@ public enum AppUtils { /** * 获取App的名称 */ - public String getAppName(Context context) { + public static String getAppName(Context context) { try{ int labelRes = getPackageInfo(context).applicationInfo.labelRes; @@ -82,7 +88,7 @@ public enum AppUtils { * @param context * @return */ - public int getAppIcon(Context context){ + public static int getAppIcon(Context context){ try{ return getPackageInfo(context).applicationInfo.icon; } catch (Exception e) { @@ -97,7 +103,19 @@ public enum AppUtils { * @param context * @param file */ - public void installApk(Context context,File file,String authority){ + public static void installApk(Context context,File file,String authority){ + Intent intent = getInstallIntent(context,file,authority); + context.startActivity(intent); + } + + /** + * 获取安装Intent + * @param context + * @param file + * @param authority + * @return + */ + public static Intent getInstallIntent(Context context,File file,String authority){ Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -111,7 +129,7 @@ public enum AppUtils { uriData = Uri.fromFile(file); } intent.setDataAndType(uriData, type); - context.startActivity(intent); + return intent; } /** @@ -122,7 +140,7 @@ public enum AppUtils { * @return * @throws Exception */ - public boolean apkExists(Context context,int versionCode,File file) throws Exception{ + public static boolean apkExists(Context context,int versionCode,File file) throws Exception{ if(file!=null && file.exists()){ String packageName = context.getPackageName(); PackageInfo packageInfo = AppUtils.getPackageInfo(context,file.getAbsolutePath()); @@ -135,4 +153,55 @@ public enum AppUtils { } return false; } + + /** + * 判断文件是否存在 + * @param context + * @param path + * @return + */ + public static boolean isAndroidQFileExists(Context context,String path){ + return isAndroidQFileExists(context,new File(path)); + } + + /** + * 判断文件是否存在 + * @param context + * @param file + * @return + */ + public static boolean isAndroidQFileExists(Context context,File file){ + AssetFileDescriptor descriptor = null; + ContentResolver contentResolver = context.getContentResolver(); + try { + Uri uri = Uri.fromFile(file); + descriptor = contentResolver.openAssetFileDescriptor(uri, "r"); + if (descriptor == null) { + return false; + } else { + close(descriptor); + } + return true; + } catch (FileNotFoundException e) { + + }finally { + close(descriptor); + } + return false; + } + + /** + * 关闭 + * @param descriptor + */ + private static void close(AssetFileDescriptor descriptor){ + if(descriptor != null){ + try { + descriptor.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } diff --git a/app-updater/src/main/java/com/king/app/updater/util/PermissionUtils.java b/app-updater/src/main/java/com/king/app/updater/util/PermissionUtils.java index f6cd5b8..a4780a7 100644 --- a/app-updater/src/main/java/com/king/app/updater/util/PermissionUtils.java +++ b/app-updater/src/main/java/com/king/app/updater/util/PermissionUtils.java @@ -1,7 +1,6 @@ package com.king.app.updater.util; import android.Manifest; -import android.annotation.TargetApi; import android.app.Activity; import android.app.AppOpsManager; import android.app.NotificationManager; @@ -18,22 +17,24 @@ import java.lang.reflect.Method; /** * @author Jenly Jenly */ -public enum PermissionUtils { +public final class PermissionUtils { - INSTANCE; - - private String[] PERMISSIONS_STORAGE = { + private static String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE}; + private PermissionUtils(){ + throw new AssertionError(); + } + /** * 校验权限 * @param activity * @param requestCode * @return */ - public boolean verifyReadAndWritePermissions(@NonNull Activity activity,int requestCode){ + public static boolean verifyReadAndWritePermissions(@NonNull Activity activity,int requestCode){ int readResult = checkPermission(activity,Manifest.permission.READ_EXTERNAL_STORAGE); int writeResult = checkPermission(activity,Manifest.permission.WRITE_EXTERNAL_STORAGE); @@ -45,7 +46,7 @@ public enum PermissionUtils { return true; } - public int checkPermission(@NonNull Activity activity,@NonNull String permission){ + public static int checkPermission(@NonNull Activity activity,@NonNull String permission){ return ActivityCompat.checkSelfPermission(activity,permission); } @@ -53,7 +54,7 @@ public enum PermissionUtils { * 获取通知权限 * @param context */ - public boolean isNotificationEnabled(Context context) { + public static boolean isNotificationEnabled(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/app-updater/src/main/java/com/king/app/updater/util/SSLSocketFactoryUtils.java b/app-updater/src/main/java/com/king/app/updater/util/SSLSocketFactoryUtils.java index 5706cea..d337736 100644 --- a/app-updater/src/main/java/com/king/app/updater/util/SSLSocketFactoryUtils.java +++ b/app-updater/src/main/java/com/king/app/updater/util/SSLSocketFactoryUtils.java @@ -24,10 +24,10 @@ import javax.net.ssl.X509TrustManager; /** * @author Jenly Jenly */ -public class SSLSocketFactoryUtils { +public final class SSLSocketFactoryUtils { private SSLSocketFactoryUtils(){ - + throw new AssertionError(); } public static SSLSocketFactory createSSLSocketFactory() { diff --git a/app-updater/src/main/res/xml/app_updater_paths.xml b/app-updater/src/main/res/xml/app_updater_paths.xml index bc154c1..0bf6011 100644 --- a/app-updater/src/main/res/xml/app_updater_paths.xml +++ b/app-updater/src/main/res/xml/app_updater_paths.xml @@ -2,10 +2,8 @@ - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 414c9d9..2dc77d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,6 +30,7 @@ dependencies { //support implementation deps.support.appcompat implementation deps.support.constraintlayout + implementation deps.okhttp implementation project(':app-updater') implementation project(':app-dialog') diff --git a/app/release/app-release.apk b/app/release/app-release.apk index f22eb05..739275e 100644 Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ diff --git a/app/release/output.json b/app/release/output.json index 670a4ef..ba0c81b 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":7,"versionName":"1.0.5","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":9,"versionName":"1.0.6","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/java/com/king/appupdater/MainActivity.java b/app/src/main/java/com/king/appupdater/MainActivity.java index 8348b34..e15336d 100644 --- a/app/src/main/java/com/king/appupdater/MainActivity.java +++ b/app/src/main/java/com/king/appupdater/MainActivity.java @@ -3,7 +3,6 @@ package com.king.appupdater; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; -import android.os.Environment; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; @@ -20,6 +19,7 @@ import com.king.app.updater.UpdateConfig; import com.king.app.updater.callback.AppUpdateCallback; import com.king.app.updater.callback.UpdateCallback; import com.king.app.updater.constant.Constants; +import com.king.app.updater.http.OkHttpManager; import com.king.app.updater.util.PermissionUtils; import java.io.File; @@ -42,8 +42,9 @@ public class MainActivity extends AppCompatActivity { setContentView(R.layout.activity_main); progressBar = findViewById(R.id.progressBar); progressBar.setVisibility(View.INVISIBLE); + progressBar.setMax(100); - PermissionUtils.INSTANCE.verifyReadAndWritePermissions(this,Constants.RE_CODE_STORAGE_PERMISSION); + PermissionUtils.verifyReadAndWritePermissions(this,Constants.RE_CODE_STORAGE_PERMISSION); } public Context getContext(){ @@ -78,6 +79,7 @@ public class MainActivity extends AppCompatActivity { config.setUrl(mUrl); config.addHeader("token","xxxxxx"); mAppUpdater = new AppUpdater(getContext(),config) + .setHttpManager(OkHttpManager.getInstance()) .setUpdateCallback(new UpdateCallback() { @Override @@ -94,10 +96,10 @@ public class MainActivity extends AppCompatActivity { } @Override - public void onProgress(int progress, int total, boolean isChange) { + public void onProgress(long progress, long total, boolean isChange) { if(isChange){ - progressBar.setMax(total); - progressBar.setProgress(progress); + int currProgress = Math.round(progress * 1.0f / total * 100.0f); + progressBar.setProgress(currProgress); } } @@ -134,7 +136,7 @@ public class MainActivity extends AppCompatActivity { .build(getContext()) .setUpdateCallback(new AppUpdateCallback() { @Override - public void onProgress(int progress, int total, boolean isChange) { + public void onProgress(long progress, long total, boolean isChange) { } @@ -214,12 +216,13 @@ public class MainActivity extends AppCompatActivity { public void onClick(View v) { mAppUpdater = new AppUpdater.Builder() .serUrl(mUrl) - .setPath(Environment.getExternalStorageDirectory() + "/.AppUpdater") +// .setPath(Environment.getExternalStorageDirectory() + "/.AppUpdater")//如果适配Android Q,则Environment.getExternalStorageDirectory()将废弃 + .setPath(getExternalFilesDir(Constants.DEFAULT_DIR).getAbsolutePath())//自定义路径 .setVersionCode(BuildConfig.VERSION_CODE)//设置versionCode之后,新版本相同的apk只下载一次,优先取本地缓存。 - .setFilename("AppUpdater1.apk") + .setFilename("AppUpdater.apk") .setVibrate(true) .build(getContext()); - mAppUpdater.start(); + mAppUpdater.setHttpManager(OkHttpManager.getInstance()).start(); AppDialog.INSTANCE.dismissDialog(); } }); @@ -243,7 +246,7 @@ public class MainActivity extends AppCompatActivity { .setVibrate(true) .setSound(true) .build(getContext()); - mAppUpdater.start(); + mAppUpdater.setHttpManager(OkHttpManager.getInstance()).start(); AppDialog.INSTANCE.dismissDialogFragment(getSupportFragmentManager()); } }); diff --git a/versions.gradle b/versions.gradle index 956f90c..a0daf94 100644 --- a/versions.gradle +++ b/versions.gradle @@ -1,7 +1,7 @@ //App def app_version = [:] -app_version.versionCode = 7 //androidx 8 -app_version.versionName = "1.0.5" +app_version.versionCode = 9 //androidx 10 +app_version.versionName = "1.0.6" ext.app_version = app_version //build version @@ -22,6 +22,9 @@ versions.constraintLayout = "1.1.3" versions.junit = "4.12" versions.runner = "1.0.2" versions.espresso = "3.0.2" + +versions.okhttp = "4.2.2" + ext.versions = versions ext.deps = [:] @@ -39,6 +42,9 @@ test.runner = "com.android.support.test:runner:$versions.runner" test.espresso = "com.android.support.test.espresso:espresso-core:$versions.espresso" deps.test = test +//okHttp +deps.okhttp = "com.squareup.okhttp3:okhttp:$versions.okhttp" + ext.deps = deps