diff --git a/README.md b/README.md index b8f7272..b5846bb 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下载 ## Gif 展示 @@ -39,7 +40,7 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 com.king.app app-updater - 1.0.5 + 1.0.6 pom @@ -47,7 +48,7 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 com.king.app app-dialog - 1.0.5 + 1.0.6 pom ``` @@ -56,25 +57,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 - + ``` @@ -127,6 +128,7 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版 .serUrl(mUrl) .setFilename("AppUpdater.apk") .build(getContext()) + .setHttpManager(OkHttpManager.getInstance())//使用OkHttpClient实现下载,需依赖okhttp库 .start(); AppDialog.INSTANCE.dismissDialogFragment(getSupportFragmentManager()); } @@ -139,6 +141,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 * 支持取消下载 @@ -184,4 +190,4 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
-
+ \ No newline at end of file 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 dfb1fba..0e00ad8 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 @@ -39,7 +39,7 @@ public enum AppDialog { * @param config 弹框配置 {@link AppDialogConfig} * @return */ - public View createAppDialogView(@NonNull Context context, @NonNull AppDialogConfig config){ + public View createAppDialogView(@NonNull Context context,@NonNull AppDialogConfig config){ View view = config.getView(context); TextView tvDialogTitle = view.findViewById(config.getTitleId()); setText(tvDialogTitle,config.getTitle()); @@ -235,8 +235,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; } }); @@ -263,4 +264,4 @@ public enum AppDialog { //------------------------------------------- -} +} \ No newline at end of file diff --git a/app-updater/build.gradle b/app-updater/build.gradle index 854a66c..c7f0cca 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 672f332..a536c5e 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 @@ -11,7 +11,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; @@ -44,7 +46,7 @@ public class AppUpdater { private ServiceConnection mServiceConnection; - public AppUpdater(@NonNull Context context, @NonNull UpdateConfig config){ + public AppUpdater(@NonNull Context context,@NonNull UpdateConfig config){ this.mContext = context; this.mConfig = config; } @@ -55,11 +57,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; @@ -72,10 +87,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."); } @@ -129,7 +144,7 @@ public class AppUpdater { } /** - * AppUpdater构建器 + * AppUpdater建造者 */ public static class Builder{ @@ -151,7 +166,7 @@ public class AppUpdater { /** * 设置保存的路径 - * @param path 下载保存的文件路径 (默认SD卡/.AppUpdater目录) + * @param path 下载保存的文件路径 * @return */ public Builder setPath(String path){ @@ -252,7 +267,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){ @@ -330,4 +345,4 @@ public class AppUpdater { } -} +} \ No newline at end of file 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..68d86c9 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"; + +} \ No newline at end of file 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 7cb17a6..45133eb 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 @@ -20,6 +20,7 @@ import javax.net.ssl.HttpsURLConnection; import androidx.annotation.Nullable; /** + * HttpManager使用{@link HttpURLConnection}实现{@link IHttpManager} * @author Jenly Jenly */ public class HttpManager implements IHttpManager { @@ -30,12 +31,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(); + } } } @@ -46,6 +49,9 @@ public class HttpManager implements IHttpManager { this(DEFAULT_TIME_OUT); } + /** + * HttpManager对外暴露。如果没有特殊需求,推荐使用{@link HttpManager#getInstance()} + */ public HttpManager(int timeout){ this.mTimeout = timeout; } @@ -64,7 +70,7 @@ public class HttpManager implements IHttpManager { /** * 异步下载任务 */ - private class DownloadTask extends AsyncTask { + private class DownloadTask extends AsyncTask { private String url; private String path; @@ -112,15 +118,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); @@ -133,7 +141,9 @@ public class HttpManager implements IHttpManager { fos.write(buffer,0,len); progress += len; //更新进度 - publishProgress(progress,length); + if(length>0){ + publishProgress(progress,length); + } } fos.flush(); @@ -177,7 +187,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()){ @@ -199,4 +209,4 @@ public class HttpManager implements IHttpManager { } -} +} \ No newline at end of file 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 74f2523..7d71d63 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 @@ -8,6 +8,7 @@ import java.util.Map; import androidx.annotation.Nullable; /** + * IHttpManager 默认提供{@link HttpManager} 和 {@link OkHttpManager}两种实现。 * @author Jenly Jenly */ public interface IHttpManager { @@ -40,7 +41,7 @@ public interface IHttpManager { * @param progress * @param total */ - void onProgress(int progress,int total); + void onProgress(long progress,long total); /** * 完成 @@ -60,4 +61,4 @@ public interface IHttpManager { */ void onCancel(); } -} +} \ No newline at end of file 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..7180c57 --- /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.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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; +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 9568fc8..3b9407a 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,10 +7,8 @@ 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.text.TextUtils; import android.util.Log; @@ -29,7 +27,7 @@ import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.core.app.NotificationCompat; -import androidx.core.content.FileProvider; +import androidx.core.content.ContextCompat; /** @@ -119,16 +117,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); @@ -136,15 +134,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); @@ -184,12 +182,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(); } /** @@ -238,8 +237,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(); } @@ -248,7 +247,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(); @@ -256,7 +255,6 @@ public class DownloadService extends Service { } - @Override public void onStart(String url) { Log.d(Constants.TAG,"onStart:" + url); @@ -272,7 +270,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(); @@ -290,7 +288,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); } } @@ -307,7 +305,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); @@ -366,7 +364,7 @@ public class DownloadService extends Service { * @param title * @param content */ - private void showStartNotification(int notifyId, String channelId, String channelName, @DrawableRes int icon, CharSequence title, CharSequence content, boolean isVibrate, boolean isSound){ + private void showStartNotification(int notifyId,String channelId, String channelName,@DrawableRes int icon,CharSequence title,CharSequence content,boolean isVibrate,boolean isSound){ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ createNotificationChannel(channelId,channelName,isVibrate,isSound); } @@ -413,18 +411,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(); @@ -469,7 +456,7 @@ public class DownloadService extends Service { } /** - * + * 取消通知 * @param notifyId */ private void cancelNotification(int notifyId){ @@ -576,4 +563,4 @@ public class DownloadService extends Service { } -} +} \ No newline at end of file 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 fba4e5e..d0a6d81 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,32 +1,38 @@ 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.text.TextUtils; import java.io.File; -import java.lang.Exception; +import java.io.FileNotFoundException; +import java.io.IOException; import androidx.core.content.FileProvider; + /** * @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); } @@ -46,7 +52,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; @@ -68,7 +74,7 @@ public enum AppUtils { /** * 获取App的名称 */ - public String getAppName(Context context) { + public static String getAppName(Context context) { try{ int labelRes = getPackageInfo(context).applicationInfo.labelRes; @@ -84,7 +90,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) { @@ -99,7 +105,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); @@ -113,7 +131,7 @@ public enum AppUtils { uriData = Uri.fromFile(file); } intent.setDataAndType(uriData, type); - context.startActivity(intent); + return intent; } /** @@ -124,7 +142,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()); @@ -137,4 +155,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 557b672..cd45154 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; @@ -19,22 +18,24 @@ import androidx.core.app.ActivityCompat; /** * @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); @@ -46,7 +47,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); } @@ -54,7 +55,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); @@ -90,4 +91,4 @@ public enum PermissionUtils { return true; } -} +} \ No newline at end of file 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 31c9811..1948c83 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 @@ -25,10 +25,10 @@ import androidx.annotation.RawRes; /** * @author Jenly Jenly */ -public class SSLSocketFactoryUtils { +public final class SSLSocketFactoryUtils { private SSLSocketFactoryUtils(){ - + throw new AssertionError(); } public static SSLSocketFactory createSSLSocketFactory() { @@ -173,4 +173,4 @@ public class SSLSocketFactoryUtils { } -} +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 021c83b..f3f9649 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 67425f2..9ef9e9d 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 38ad7a2..7f0091e 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":8,"versionName":"1.0.5-androidx","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":10,"versionName":"1.0.6-androidx","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 76b29b4..5ef60e3 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.view.LayoutInflater; import android.view.View; import android.widget.Button; @@ -18,6 +17,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; @@ -25,6 +25,9 @@ import java.io.File; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +/** + * @author Jenly + */ public class MainActivity extends AppCompatActivity { private final Object mLock = new Object(); @@ -43,8 +46,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(){ @@ -79,6 +83,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 @@ -95,10 +100,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); } } @@ -135,7 +140,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) { } @@ -215,12 +220,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(); } }); @@ -244,7 +250,7 @@ public class MainActivity extends AppCompatActivity { .setVibrate(true) .setSound(true) .build(getContext()); - mAppUpdater.start(); + mAppUpdater.setHttpManager(OkHttpManager.getInstance()).start(); AppDialog.INSTANCE.dismissDialogFragment(getSupportFragmentManager()); } }); @@ -287,4 +293,4 @@ public class MainActivity extends AppCompatActivity { break; } } -} +} \ No newline at end of file diff --git a/versions.gradle b/versions.gradle index cffd7db..e8d9d24 100644 --- a/versions.gradle +++ b/versions.gradle @@ -1,7 +1,7 @@ //App def app_version = [:] -app_version.versionCode = 8 //androidx 8 -app_version.versionName = "1.0.5-androidx" +app_version.versionCode = 10 //androidx 10 +app_version.versionName = "1.0.6-androidx" ext.app_version = app_version //build version @@ -23,6 +23,9 @@ versions.junit = "1.1.0" versions.test = "1.2.0" versions.runner = "1.2.0" versions.espresso = "3.2.0" + +versions.okhttp = "4.2.2" + ext.versions = versions ext.deps = [:] @@ -41,6 +44,8 @@ test.runner = "androidx.test:runner:$versions.runner" test.espresso = "androidx.test.espresso:espresso-core:$versions.espresso" deps.test = test +//okHttp +deps.okhttp = "com.squareup.okhttp3:okhttp:$versions.okhttp" ext.deps = deps