优化细节

master
Jenly 2 years ago
parent 08e5c8fbb6
commit 7f23dfb4ef
  1. 15
      README.md
  2. 96
      app-dialog/src/main/java/com/king/app/dialog/AppDialog.java
  3. 362
      app-dialog/src/main/java/com/king/app/dialog/AppDialogConfig.java
  4. 218
      app-dialog/src/main/java/com/king/app/dialog/BaseDialogConfig.java
  5. 2
      app-dialog/src/main/java/com/king/app/dialog/fragment/AppDialogFragment.java
  6. 27
      app-dialog/src/main/java/com/king/app/dialog/fragment/BaseDialogFragment.java
  7. 5
      app-updater/src/main/AndroidManifest.xml
  8. 9
      app-updater/src/main/java/com/king/app/updater/AppUpdater.java
  9. 8
      app-updater/src/main/java/com/king/app/updater/UpdateConfig.java
  10. 2
      app-updater/src/main/java/com/king/app/updater/constant/Constants.java
  11. 40
      app-updater/src/main/java/com/king/app/updater/http/HttpManager.java
  12. 2
      app-updater/src/main/java/com/king/app/updater/http/IHttpManager.java
  13. 28
      app-updater/src/main/java/com/king/app/updater/http/OkHttpManager.java
  14. 25
      app-updater/src/main/java/com/king/app/updater/notify/INotification.java
  15. 38
      app-updater/src/main/java/com/king/app/updater/notify/NotificationImpl.java
  16. 233
      app-updater/src/main/java/com/king/app/updater/service/DownloadService.java
  17. 18
      app-updater/src/main/java/com/king/app/updater/util/AppUtils.java
  18. 316
      app-updater/src/main/java/com/king/app/updater/util/LogUtils.java
  19. 26
      app-updater/src/main/java/com/king/app/updater/util/NotificationUtils.java
  20. 34
      app-updater/src/main/java/com/king/app/updater/util/PermissionUtils.java
  21. 62
      app-updater/src/main/java/com/king/app/updater/util/SSLSocketFactoryUtils.java
  22. BIN
      app/release/app-release.apk
  23. 4
      app/release/output-metadata.json
  24. 3
      app/src/main/AndroidManifest.xml
  25. 18
      app/src/main/java/com/king/appupdater/MainActivity.java
  26. 4
      gradle.properties
  27. 4
      versions.gradle

@ -28,9 +28,11 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
- [x] 支持下载失败,重新下载
- [x] 支持下载优先取本地缓存
- [x] 支持通知栏提示内容和过程全部可配置
- [x] 支持Android Q(10)
- [x] 支持取消下载
- [x] 支持使用OkHttpClient下载
- [x] 支持Android 10(Q)
- [x] 支持Android 11(R)
- [x] 支持Android 12(S)
## Gif 展示
![Image](GIF.gif)
@ -39,8 +41,6 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
## 引入
> 由于2021年2月3日 **JFrog宣布将关闭Bintray和JCenter,计划在2022年2月完全关闭。** 所以后续版本不再发布至 **JCenter**
### Gradle:
1. 在Project的 **build.gradle** 里面添加远程仓库
@ -60,9 +60,9 @@ allprojects {
//----------AndroidX 版本
//app-updater
implementation 'com.github.jenly1314.AppUpdater:app-updater:1.1.2'
implementation 'com.github.jenly1314.AppUpdater:app-updater:1.1.3'
//app-dialog
implementation 'com.github.jenly1314.AppUpdater:app-dialog:1.1.2'
implementation 'com.github.jenly1314.AppUpdater:app-dialog:1.1.3'
```
@ -140,6 +140,11 @@ allprojects {
## 版本记录
#### v1.1.3:2021-04-25
* 统一日志管理
* 适配Android 12(S)
* 优化细节
#### v1.1.2:2021-11-18
* AppDialog对外提供更多与WindowManager.LayoutParams相关的配置

@ -16,6 +16,7 @@ import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
/**
* App对话框封装便捷的对话框API使用时更简单
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public enum AppDialog {
@ -40,11 +41,20 @@ public enum AppDialog {
//-------------------------------------------
/**
* {@link DialogFragment#dismiss()}
* @param fragmentManager {@link FragmentManager}
*/
public void dismissDialogFragment(FragmentManager fragmentManager){
dismissDialogFragment(fragmentManager,mTag);
mTag = null;
}
/**
* {@link DialogFragment#dismiss()}
* @param fragmentManager {@link FragmentManager}
* @param tag dialogFragment对应的标签
*/
public void dismissDialogFragment(FragmentManager fragmentManager,String tag){
if(tag!=null){
DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag(tag);
@ -52,8 +62,12 @@ public enum AppDialog {
}
}
/**
* {@link DialogFragment#dismiss()}
* @param dialogFragment {@link DialogFragment}
*/
public void dismissDialogFragment(DialogFragment dialogFragment){
if(dialogFragment!=null){
if(dialogFragment != null){
dialogFragment.dismiss();
}
}
@ -62,7 +76,8 @@ public enum AppDialog {
/**
* 显示DialogFragment
* @param fragmentManager
* @param fragmentManager {@link FragmentManager}
* @param config {@link AppDialogConfig}
* @return
*/
public String showDialogFragment(FragmentManager fragmentManager,AppDialogConfig config){
@ -75,8 +90,8 @@ public enum AppDialog {
/**
* 显示DialogFragment
* @param fragmentManager
* @param dialogFragment
* @param fragmentManager {@link FragmentManager}
* @param dialogFragment {@link DialogFragment}
* @return
*/
public String showDialogFragment(FragmentManager fragmentManager,DialogFragment dialogFragment){
@ -88,9 +103,9 @@ public enum AppDialog {
/**
* 显示DialogFragment
* @param fragmentManager
* @param dialogFragment
* @param tag
* @param fragmentManager {@link FragmentManager}
* @param dialogFragment {@link DialogFragment}
* @param tag dialogFragment对应的标签
* @return
*/
public String showDialogFragment(FragmentManager fragmentManager,DialogFragment dialogFragment, String tag) {
@ -121,7 +136,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param config 弹框配置 {@link AppDialogConfig}
*/
public void showDialog(Context context,AppDialogConfig config){
@ -130,7 +145,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param config 弹框配置 {@link AppDialogConfig}
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
@ -142,7 +157,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
*/
public void showDialog(Context context,View contentView){
@ -151,7 +166,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
@ -161,7 +176,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param widthRatio 宽度比例根据屏幕宽度计算得来
*/
@ -171,7 +186,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param isCancel 是否可取消默认为truefalse则拦截back键
@ -183,7 +198,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
@ -194,7 +209,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
@ -205,7 +220,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
@ -217,7 +232,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -231,7 +246,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -247,7 +262,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -265,7 +280,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -287,8 +302,8 @@ public enum AppDialog {
/**
* 设置弹框窗口配置
* @param context
* @param dialog
* @param context 上下文
* @param dialog Dialog对话框
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
@ -331,7 +346,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param config 弹框配置 {@link AppDialogConfig}
*/
public Dialog createDialog(Context context,AppDialogConfig config){
@ -340,7 +355,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param config 弹框配置 {@link AppDialogConfig}
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
@ -350,7 +365,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
*/
public Dialog createDialog(Context context,View contentView){
@ -359,7 +374,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
@ -369,7 +384,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param widthRatio 宽度比例根据屏幕宽度计算得来
*/
@ -379,7 +394,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param isCancel 是否可取消默认为truefalse则拦截back键
@ -390,7 +405,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
@ -401,7 +416,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -414,7 +429,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
@ -426,7 +441,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -439,7 +454,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -455,7 +470,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -473,7 +488,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
@ -508,15 +523,26 @@ public enum AppDialog {
return dialog;
}
/**
* 获取Dialog
* @return {@link #mDialog}
*/
public Dialog getDialog(){
return mDialog;
}
/**
* {@link Dialog#dismiss()}
*/
public void dismissDialog(){
dismissDialog(mDialog);
mDialog = null;
}
/**
* {@link Dialog#dismiss()}
* @param dialog {@link Dialog}
*/
public void dismissDialog(Dialog dialog){
if(dialog != null){
dialog.dismiss();

@ -29,6 +29,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
/**
* App 对话框配置
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class AppDialogConfig extends BaseDialogConfig{
@ -41,24 +42,37 @@ public class AppDialogConfig extends BaseDialogConfig{
private ViewHolder viewHolder;
/**
* 构造
* @param context 上下文
*/
public AppDialogConfig(@NonNull Context context){
this(context, R.layout.app_dialog);
}
/**
* 构造
* @param context 上下文
* @param layoutId 布局ID
*/
public AppDialogConfig(@NonNull Context context, @LayoutRes int layoutId){
super(layoutId);
this.context = context;
views = new SparseArray<>();
}
/**
* 获取上下文
* @return {@link #context}
*/
public Context getContext(){
return context;
}
/**
*
* @param context
* @return
* 获取对话框视图
* @param context 上下文
* @return 对话框视图
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
@Deprecated
@ -66,6 +80,10 @@ public class AppDialogConfig extends BaseDialogConfig{
return getDialogView();
}
/**
* 获取对话框视图
* @return 对话框视图
*/
private View getDialogView(){
if(view == null){
view = LayoutInflater.from(context).inflate(getLayoutId(),null);
@ -73,10 +91,22 @@ public class AppDialogConfig extends BaseDialogConfig{
return view;
}
/**
* 通过视图ID查找对应的视图
* @param id 视图ID
* @param <T> 对应的视图类
* @return 视图ID对应的视图
*/
private <T extends View> T findView(@IdRes int id){
return getDialogView().findViewById(id);
}
/**
* 根据视图ID获取对应的视图
* @param id 视图ID
* @param <T> 对应的视图类
* @return 视图ID对应的视图
*/
public <T extends View> T getView(@IdRes int id){
View v = views.get(id);
if(v == null){
@ -89,7 +119,7 @@ public class AppDialogConfig extends BaseDialogConfig{
/**
* 通过{@link AppDialogConfig} 创建一个视图
* @return
* @return {@link View}
*/
View buildAppDialogView(){
TextView tvDialogTitle = getView(titleId);
@ -125,6 +155,7 @@ public class AppDialogConfig extends BaseDialogConfig{
return view;
}
private void setText(TextView tv, CharSequence text){
if(text != null){
tv.setText(text);
@ -154,12 +185,26 @@ public class AppDialogConfig extends BaseDialogConfig{
//---------------------- 控件常用设置
/**
* 设置视图的背景色
* {@link View#setBackgroundResource(int)}
* @param id 视图ID
* @param resId Drawable资源ID
* @return {@link View}
*/
public View setBackgroundResource(@IdRes int id,@DrawableRes int resId){
View v = getView(id);
v.setBackgroundResource(resId);
return v;
}
/**
* 设置视图的背景色
* {@link View#setBackground(Drawable)}
* @param id 视图ID
* @param drawable {@link Drawable}
* @return {@link View}
*/
@TargetApi(16)
public View setBackground(@IdRes int id, Drawable drawable){
View v = getView(id);
@ -167,30 +212,66 @@ public class AppDialogConfig extends BaseDialogConfig{
return v;
}
/**
* 设置视图的背景色
* {@link View#setBackgroundColor(int)}
* @param id 视图ID
* @param color 颜色
* @return {@link View}
*/
public View setBackgroundColor(@IdRes int id,@ColorInt int color){
View v = getView(id);
v.setBackgroundColor(color);
return v;
}
/**
* 设置视图的标签
* {@link View#setTag(Object)}
* @param id 视图ID
* @param tag 标签
* @return {@link View}
*/
public View setTag(@IdRes int id,Object tag){
View v = getView(id);
v.setTag(tag);
return v;
}
/**
* 设置视图的标签
* {@link View#setTag(int, Object)}
* @param id 视图ID
* @param key 标签的key
* @param tag 标签
* @return {@link View}
*/
public View setTag(@IdRes int id,int key,Object tag){
View v = getView(id);
v.setTag(key,tag);
return v;
}
public View setVisibility(@IdRes int id,int visibility){
/**
* 设置视图的可见性
* {@link View#setVisibility(int)}
* @param id 视图ID
* @param visibility 可见性
* @return {@link View}
*/
public View setVisibility(@IdRes int id, int visibility){
View v = getView(id);
v.setVisibility(visibility);
return v;
}
/**
* 设置视图的可见性
* {@link View#setVisibility(int)}
* @param id 视图ID
* @param isVisible 是否可见
* @return {@link View}
*/
public View setVisibility(@IdRes int id,boolean isVisible){
View v = getView(id);
if(isVisible){
@ -201,162 +282,371 @@ public class AppDialogConfig extends BaseDialogConfig{
return v;
}
/**
* 设置视图的透明度
* {@link View#setAlpha(float)}
* @param id 视图ID
* @param alpha 透明度
* @return {@link View}
*/
public View setAlpha(@IdRes int id,float alpha){
View v = getView(id);
v.setAlpha(alpha);
return v;
}
/**
* 设置视图左方的复合绘图 {@link Drawable}
* {@link #setCompoundDrawables(int, Drawable, Drawable, Drawable, Drawable)}
* @param id 视图ID
* @param drawable {@link Drawable}
* @return {@link TextView}
*/
public TextView setCompoundDrawableLeft(@IdRes int id,Drawable drawable){
return setCompoundDrawables(id,drawable,null,null,null);
}
/**
* 设置视图上方的复合绘图 {@link Drawable}
* {@link #setCompoundDrawables(int, Drawable, Drawable, Drawable, Drawable)}
* @param id 视图ID
* @param drawable {@link Drawable}
* @return {@link TextView}
*/
public TextView setCompoundDrawableTop(@IdRes int id,Drawable drawable){
return setCompoundDrawables(id,null,drawable,null,null);
}
/**
* 设置视图右方的复合绘图 {@link Drawable}
* {@link #setCompoundDrawables(int, Drawable, Drawable, Drawable, Drawable)}
* @param id 视图ID
* @param drawable {@link Drawable}
* @return {@link TextView}
*/
public TextView setCompoundDrawableRight(@IdRes int id,Drawable drawable){
return setCompoundDrawables(id,null,null,drawable,null);
}
/**
* 设置视图下方的复合绘图 {@link Drawable}
* {@link #setCompoundDrawables(int, Drawable, Drawable, Drawable, Drawable)}
* @param id 视图ID
* @param drawable {@link Drawable}
* @return {@link TextView}
*/
public TextView setCompoundDrawableBottom(@IdRes int id,Drawable drawable){
return setCompoundDrawables(id,null,null,null,drawable);
}
/**
* 设置视图的复合绘图 {@link Drawable}
* {@link TextView#setCompoundDrawables(Drawable, Drawable, Drawable, Drawable)}
* @param id 视图ID
* @param left 左方的Drawable
* @param top 上方的Drawable
* @param right 右方的Drawable
* @param bottom 下方的Drawable
* @return {@link TextView}
*/
public TextView setCompoundDrawables(@IdRes int id,Drawable left,Drawable top,Drawable right,Drawable bottom){
TextView tv = getView(id);
tv.setCompoundDrawables(left, top, right, bottom);
return tv;
}
/**
* 设置视图的可填充内距
* {@link TextView#setCompoundDrawablePadding(int)}
* @param id 视图ID
* @param padding 内填充间距
* @return
*/
public TextView setCompoundDrawablePadding(@IdRes int id,int padding){
TextView tv = getView(id);
tv.setCompoundDrawablePadding(padding);
return tv;
}
/**
* 设置视图的内在的复合绘图 {@link Drawable}
* {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}
* @param id 视图ID
* @param left 左方的Drawable
* @param top 上方的Drawable
* @param right 右方的Drawable
* @param bottom 下方的Drawable
* @return {@link TextView}
*/
public TextView setCompoundDrawablesWithIntrinsicBounds(@IdRes int id, @Nullable Drawable left,@Nullable Drawable top,@Nullable Drawable right,@Nullable Drawable bottom){
TextView tv = getView(id);
tv.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
return tv;
}
/**
* 设置文本内容
* {@link TextView#setText(int)}
* @param id 视图ID
* @param resId 字符串资源ID
* @return {@link TextView}
*/
public TextView setText(@IdRes int id,@StringRes int resId){
TextView tv = getView(id);
tv.setText(resId);
return tv;
}
/**
* 设置文本内容
* {@link TextView#setText(CharSequence)}
* @param id 视图ID
* @param text 文本
* @return {@link TextView}
*/
public TextView setText(@IdRes int id,CharSequence text){
TextView tv = getView(id);
tv.setText(text);
return tv;
}
/**
* 设置字体颜色
* {@link TextView#setTextColor(int)}
* @param id 视图ID
* @param color 颜色
* @return {@link TextView}
*/
public TextView setTextColor(@IdRes int id,int color){
TextView tv = getView(id);
tv.setTextColor(color);
return tv;
}
/**
* 设置字体颜色
* {@link TextView#setTextColor(ColorStateList)}
* @param id 视图ID
* @param colors 颜色状态列表
* @return {@link TextView}
*/
public TextView setTextColor(@IdRes int id,@NonNull ColorStateList colors){
TextView tv = getView(id);
tv.setTextColor(colors);
return tv;
}
/**
* 设置字体大小
* {@link TextView#setTextSize(float)}
* @param id 视图ID
* @param size 字体大小单位sp
* @return {@link TextView}
*/
public TextView setTextSize(@IdRes int id,float size){
return setTextSize(id,TypedValue.COMPLEX_UNIT_SP, size);
return setTextSize(id, size);
}
/**
* 设置字体大小
* {@link TextView#setTextSize(int, float)}
* @param id 视图ID
* @param unit 单位推荐使用 {@link TypedValue#COMPLEX_UNIT_SP}
* @param size 字体大小
* @return {@link TextView}
*/
public TextView setTextSize(@IdRes int id,int unit, float size){
TextView tv = getView(id);
tv.setTextSize(unit,size);
return tv;
}
/**
* 设置字体
* {@link TextView#setTypeface(Typeface)}
* @param id 视图ID
* @param tf 字体
* @return {@link TextView}
*/
public TextView setTypeface(@IdRes int id,@Nullable Typeface tf){
TextView tv = getView(id);
tv.setTypeface(tf);
return tv;
}
/**
* 设置字体
* {@link TextView#setTypeface(Typeface, int)}
* @param id 视图ID
* @param tf 字体
* @param style 字体样式
* @return {@link TextView}
*/
public TextView setTypeface(@IdRes int id,@Nullable Typeface tf, int style){
TextView tv = getView(id);
tv.setTypeface(tf,style);
return tv;
}
/**
* 添加链接
* {@link #addLinks(int, int)}
* @param id 视图ID
* @return {@link TextView}
*/
public TextView addLinks(@IdRes int id){
return addLinks(id,Linkify.ALL);
}
/**
* 添加链接
* {@link Linkify#addLinks(TextView, int)}
* @param id 视图ID
* @param mask 连接掩码{@link Linkify#ALL}
* @return {@link TextView}
*/
public TextView addLinks(@IdRes int id,int mask){
TextView tv = getView(id);
Linkify.addLinks(tv,mask);
return tv;
}
/**
* 添加链接
* {@link Linkify#addLinks(TextView, Pattern, String)}
* @param id 视图ID
* @param pattern 正则表达式模式
* @param scheme 方案
* @return {@link TextView}
*/
public TextView addLinks(@IdRes int id,@NonNull Pattern pattern,@Nullable String scheme){
TextView tv = getView(id);
Linkify.addLinks(tv,pattern,scheme);
return tv;
}
/**
* 根据Drawable资源ID设置图像
* {@link ImageView#setImageResource(int)}
* @param id 视图ID
* @param resId Drawable资源ID
* @return {@link ImageView}
*/
public ImageView setImageResource(@IdRes int id,@DrawableRes int resId){
ImageView iv = getView(id);
iv.setImageResource(resId);
return iv;
}
/**
* 根据位图设置图像
* {@link ImageView#setImageBitmap(Bitmap)}
* @param id 视图ID
* @param bitmap 位图
* @return {@link ImageView}
*/
public ImageView setImageBitmap(@IdRes int id, Bitmap bitmap){
ImageView iv = getView(id);
iv.setImageBitmap(bitmap);
return iv;
}
/**
* 根据 {@link Drawable} 设置图像
* {@link ImageView#setImageResource(int)}
* @param id 视图ID
* @param drawable {@link Drawable}
* @return {@link ImageView}
*/
public ImageView setImageDrawable(@IdRes int id,Drawable drawable){
ImageView iv = getView(id);
iv.setImageDrawable(drawable);
return iv;
}
/**
* 设置是否选中
* {@link CompoundButton#setChecked(boolean)}
* @param id 视图ID
* @param isChecked 是否选中
* @return {@link CompoundButton}
*/
public CompoundButton setChecked(@IdRes int id, boolean isChecked){
CompoundButton cb = getView(id);
cb.setChecked(isChecked);
return cb;
}
/**
* 是否选中
* {@link CompoundButton#isChecked()}
* @param id 视图ID
* @return {@code true} or {@code false}
*/
public boolean isChecked(@IdRes int id){
CompoundButton cb = getView(id);
return cb.isChecked();
}
/**
* 切换
* {@link CompoundButton#toggle()}
* @param id 视图ID
* @return {@link CompoundButton}
*/
public CompoundButton toggle(@IdRes int id){
CompoundButton cb = getView(id);
cb.toggle();
return cb;
}
/**
* 设置进度值
* {@link ProgressBar#setProgress(int)}
* @param id 视图ID
* @param progress 进度
* @return {@link ProgressBar}
*/
public ProgressBar setProgress(@IdRes int id, int progress){
ProgressBar progressBar = getView(id);
progressBar.setProgress(progress);
return progressBar;
}
/**
* 设置最大进度值
* {@link ProgressBar#setMax(int)}
* @param id 视图ID
* @param max 最大进度值
* @return {@link ProgressBar}
*/
public ProgressBar setMax(@IdRes int id,int max){
ProgressBar progressBar = getView(id);
progressBar.setMax(max);
return progressBar;
}
/**
* 设置评分
* {@link RatingBar#setRating(float)}
* @param id 视图ID
* @param rating 评分
* @return {@link RatingBar}
*/
public RatingBar setRating(@IdRes int id, float rating){
RatingBar ratingBar = getView(id);
ratingBar.setRating(rating);
return ratingBar;
}
/**
* 设置评分和最大评分值
* {@link RatingBar#setRating(float)} and {@link RatingBar#setMax(int)}
* @param id 视图ID
* @param rating 评分
* @param max 最大评分值
* @return {@link RatingBar}
*/
public RatingBar setRating(@IdRes int id,float rating,int max){
RatingBar ratingBar = getView(id);
ratingBar.setRating(rating);
@ -364,28 +654,61 @@ public class AppDialogConfig extends BaseDialogConfig{
return ratingBar;
}
/**
* 设置星星数量
* {@link RatingBar#setNumStars(int)}
* @param id 视图ID
* @param numStars 星星数量
* @return {@link RatingBar}
*/
public RatingBar setNumStars(@IdRes int id,int numStars){
RatingBar ratingBar = getView(id);
ratingBar.setNumStars(numStars);
return ratingBar;
}
/**
* 设置是否选择
* {@link View#setSelected(boolean)}
* @param id 视图ID
* @param selected 是否选择
* @return {@link View}
*/
public View setSelected(@IdRes int id,boolean selected){
View view = getView(id);
view.setSelected(selected);
return view;
}
/**
* 是否选择
* {@link View#isSelected()}
* @param id 视图ID
* @return {@code true} or {@code false}
*/
public boolean isSelected(@IdRes int id){
return getView(id).isSelected();
}
/**
* 设置是否启用
* {@link View#setEnabled(boolean)}
* @param id 视图ID
* @param enabled 是否启用
* @return {@link View}
*/
public View setEnabled(@IdRes int id,boolean enabled){
View view = getView(id);
view.setEnabled(enabled);
return view;
}
/**
* 是否启用
* {@link View#isEnabled()}
* @param id 视图ID
* @return {@code true} or {@code false}
*/
public boolean isEnabled(@IdRes int id){
return getView(id).isEnabled();
}
@ -393,19 +716,42 @@ public class AppDialogConfig extends BaseDialogConfig{
//---------------------- 监听事件
/**
* 设置点击监听事
* {@link View#setOnClickListener(View.OnClickListener)}
* @param id 视图ID
* @param onClickListener {@link View.OnClickListener}
*/
public void setOnClickListener(@IdRes int id, View.OnClickListener onClickListener){
getView(id).setOnClickListener(onClickListener);
}
/**
* 设置触摸监听
* {@link View#setOnTouchListener(View.OnTouchListener)}
* @param id 视图ID
* @param onTouchListener {@link View.OnTouchListener}
*/
public void setOnTouchListener(@IdRes int id, View.OnTouchListener onTouchListener){
getView(id).setOnTouchListener(onTouchListener);
}
/**
* 设置长按监听
* {@link View#setOnLongClickListener(View.OnLongClickListener)}
* @param id 视图ID
* @param onLongClickListener {@link View.OnLongClickListener}
*/
public void setOnLongClickListener(@IdRes int id, View.OnLongClickListener onLongClickListener){
getView(id).setOnLongClickListener(onLongClickListener);
}
/**
* 设置按键监听
* {@link View#setOnKeyListener(View.OnKeyListener)}
* @param id 视图ID
* @param onKeyListener {@link View.OnKeyListener}
*/
public void setOnKeyListener(@IdRes int id, View.OnKeyListener onKeyListener){
getView(id).setOnKeyListener(onKeyListener);
}

@ -12,6 +12,7 @@ import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
/**
* 基础对话框配置
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class BaseDialogConfig {
@ -116,7 +117,9 @@ public class BaseDialogConfig {
View.OnClickListener onClickConfirm;
/**
* 构造
*/
public BaseDialogConfig(){
this(R.layout.app_dialog);
}
@ -129,15 +132,18 @@ public class BaseDialogConfig {
this.layoutId = layoutId;
}
/**
* 布局ID
* @return 布局ID
*/
public @LayoutRes int getLayoutId() {
return layoutId;
}
/**
* 此方法即将废弃请通过构造{@link #BaseDialogConfig(int)}来初始化
* @param layoutId
* @return
* @param layoutId 布局ID
* @return {@link BaseDialogConfig}
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
@Deprecated
@ -146,20 +152,28 @@ public class BaseDialogConfig {
return this;
}
/**
* 标题视图ID
* @return 视图ID
*/
public int getTitleId() {
return titleId;
}
/**
* 设置标题布局ID
* @param titleId
* @return
* 设置标题视图ID
* @param titleId 视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setTitleId(@IdRes int titleId) {
this.titleId = titleId;
return this;
}
/**
* 样式ID
* @return 样式ID
*/
public int getStyleId() {
return styleId;
}
@ -167,13 +181,17 @@ public class BaseDialogConfig {
/**
* 设置Dialog样式ID(仅对Dialog有效如果使用的是DialogFragment请使用{@link #setAnimationStyleId(int)})
* @param styleId
* @return
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setStyleId(@StyleRes int styleId) {
this.styleId = styleId;
return this;
}
/**
* 对话框动画样式ID
* @return
*/
public int getAnimationStyleId(){
return animationStyleId;
}
@ -181,35 +199,43 @@ public class BaseDialogConfig {
/**
* 对话框动画样式ID (仅对DialogFragment有效如果使用的是Dialog请使用{@link #setStyleId(int)})
* @param animationStyleId
* @return
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setAnimationStyleId(@StyleRes int animationStyleId) {
this.animationStyleId = animationStyleId;
return this;
}
/**
* 内容视图ID
* @return 视图ID
*/
public @IdRes int getContentId() {
return contentId;
}
/**
* 设置内容布局ID
* @param contentId
* @return
* 设置内容视图ID
* @param contentId 内容视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setContentId(@IdRes int contentId) {
this.contentId = contentId;
return this;
}
/**
* 取消按钮视图ID
* @return 视图ID
*/
public @IdRes int getCancelId() {
return cancelId;
}
/**
* 设置取消按钮布局ID
* @param cancelId
* @return
* 设置取消按钮视图ID
* @param cancelId 取消按钮视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancelId(@IdRes int cancelId) {
this.cancelId = cancelId;
@ -217,8 +243,8 @@ public class BaseDialogConfig {
}
/**
* 获取确定按钮布局ID
* @return
* 获取确定按钮视图ID
* @return 确定按钮视图ID
* @Deprecated 请使用 {@link #getConfirmId()}来代替后续版本可能会移除此方法
*/
@Deprecated
@ -227,9 +253,9 @@ public class BaseDialogConfig {
}
/**
* 设置确定按钮布局ID
* @param okId
* @return
* 设置确定按钮视图ID
* @param okId 确定按钮视图ID
* @return {@link BaseDialogConfig}
* @Deprecated 请使用 {@link #setConfirmId(int)}来代替后续版本可能移除此方法
*/
@Deprecated
@ -238,45 +264,53 @@ public class BaseDialogConfig {
}
/**
* 获取确定按钮布局ID
* @return
* 获取确定按钮视图ID
* @return 视图ID
*/
public @IdRes int getConfirmId() {
return confirmId;
}
/**
* 设置确定按钮布局ID
* @param confirmId
* @return
* 设置确定按钮视图ID
* @param confirmId 确定按钮视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setConfirmId(@IdRes int confirmId) {
this.confirmId = confirmId;
return this;
}
/**
* 分割线视图ID
* @return 视图ID
*/
public @IdRes int getLineId() {
return lineId;
}
/**
* 设置分割线布局ID
* @param lineId
* @return
* 设置分割线视图ID
* @param lineId 分割线视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setLineId(@IdRes int lineId) {
this.lineId = lineId;
return this;
}
/**
* 标题
* @return 标题
*/
public CharSequence getTitle() {
return title;
}
/**
* 设置标题
* @param title
* @return
* @param title 标题
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setTitle(CharSequence title) {
this.title = title;
@ -285,37 +319,45 @@ public class BaseDialogConfig {
/**
* 设置标题
* @param context
* @param resId
* @return
* @param context 上下文
* @param resId 标题资源ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setTitle(@NonNull Context context, @StringRes int resId) {
this.title = context.getString(resId);
return this;
}
/**
* 文本内容
* @return 文本内容
*/
public CharSequence getContent() {
return content;
}
/**
* 设置文本内容
* @param content
* @return
* @param content 文本内容
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setContent(CharSequence content) {
this.content = content;
return this;
}
/**
* 取消按钮文本内容
* @return 取消按钮文本内容
*/
public CharSequence getCancel() {
return cancel;
}
/**
* 设置取消按钮文本内容
* @param cancel
* @return
* @param cancel 取消按钮文本内容
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancel(CharSequence cancel) {
this.cancel = cancel;
@ -324,9 +366,9 @@ public class BaseDialogConfig {
/**
* 设置取消按钮文本内容
* @param context
* @param resId
* @return
* @param context 上下文
* @param resId 取消按钮文本内容资源ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancel(@NonNull Context context, @StringRes int resId) {
this.cancel = context.getString(resId);
@ -335,7 +377,7 @@ public class BaseDialogConfig {
/**
* 获取确定按钮文本内容
* @return
* @return 确定按钮文本内容
* @deprecated 请使用 {@link #getConfirm()} 来代替后续版本可能会移除此方法
*/
public CharSequence getOk() {
@ -344,8 +386,8 @@ public class BaseDialogConfig {
/**
* 设置确定按钮文本内容
* @param ok
* @return
* @param ok 确定按钮文本内容
* @return {@link BaseDialogConfig}
* @deprecated 请使用 {@link #setConfirm(CharSequence)} 来代替后续版本可能会移除此方法
*/
public BaseDialogConfig setOk(CharSequence ok) {
@ -354,23 +396,27 @@ public class BaseDialogConfig {
/**
* 设置确定按钮文本内容
* @param context
* @param resId
* @return
* @param context 上下文
* @param resId 确定按钮文本内容资源ID
* @return {@link BaseDialogConfig}
* @deprecated 请使用 {@link #setConfirm(Context, int)}来代替后续版本可能会移除此方法
*/
public BaseDialogConfig setOk(@NonNull Context context, @StringRes int resId) {
return setConfirm(context,resId);
}
/**
* 确定按钮文本内容
* @return 确定按钮文本内容
*/
public CharSequence getConfirm() {
return confirm;
}
/**
* 设置确定按钮文本内容
* @param confirm
* @return
* @param confirm 确定按钮文本内容
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setConfirm(CharSequence confirm) {
this.confirm = confirm;
@ -379,9 +425,9 @@ public class BaseDialogConfig {
/**
* 设置确定按钮文本内容
* @param context
* @param resId
* @return
* @param context 上下文
* @param resId 确定按钮文本内容资源ID
* @return {@link BaseDialogConfig}
*
*/
public BaseDialogConfig setConfirm(@NonNull Context context, @StringRes int resId) {
@ -389,84 +435,108 @@ public class BaseDialogConfig {
return this;
}
/**
* 是否隐藏取消按钮
* @return {@link #isHideCancel}
*/
public boolean isHideCancel() {
return isHideCancel;
}
/**
* 设置是否隐藏取消按钮
* @param hideCancel
* @return
* @param hideCancel 是否隐藏取消按钮
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setHideCancel(boolean hideCancel) {
isHideCancel = hideCancel;
return this;
}
/**
* 是否隐藏标题
* @return {@link #isHideTitle}
*/
public boolean isHideTitle(){
return isHideTitle;
}
/**
* 设置是否隐藏标题
* @param hideTitle
* @return
* @param hideTitle 是否隐藏标题
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setHideTitle(boolean hideTitle){
isHideTitle = hideTitle;
return this;
}
/**
* Dialog的宽度比例根据屏幕宽度计算得来
* @return {@link #widthRatio}
*/
public float getWidthRatio() {
return widthRatio;
}
/**
* 设置Dialog的宽度比例根据屏幕宽度计算得来
* @param widthRatio
* @return
* @param widthRatio Dialog的宽度比例默认值{@link AppDialog#DEFAULT_WIDTH_RATIO}
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setWidthRatio(float widthRatio){
this.widthRatio = widthRatio;
return this;
}
/**
* Dialog的对齐方式 {@link WindowManager.LayoutParams#gravity}
* @return Dialog的对齐方式
*/
public int getGravity() {
return gravity;
}
/**
* 设置Dialog的对齐方式 {@link WindowManager.LayoutParams#gravity}
* @param gravity
* @return
* @param gravity Dialog的对齐方式
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setGravity(int gravity){
this.gravity = gravity;
return this;
}
/**
* 取消按钮点击监听不设置默认点击关闭弹框
* @return 取消按钮点击监听
*/
public View.OnClickListener getOnClickCancel() {
return onClickCancel;
}
/**
* 设置取消按钮点击监听不设置默认点击关闭弹框
* @param onClickCancel
* @return
* @param onClickCancel 取消按钮点击监听
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setOnClickCancel(View.OnClickListener onClickCancel) {
this.onClickCancel = onClickCancel;
return this;
}
/**
* 确定按钮点击监听不设置默认点击关闭弹框
* @return 确定按钮点击监听
*/
public View.OnClickListener getOnClickConfirm() {
return onClickConfirm;
}
/**
* 设置确定按钮点击监听不设置默认点击关闭弹框
* @param onClickConfirm
* @return
* @param onClickConfirm 确定按钮点击监听
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setOnClickConfirm(View.OnClickListener onClickConfirm) {
this.onClickConfirm = onClickConfirm;
@ -476,7 +546,7 @@ public class BaseDialogConfig {
/**
* 获取确定按钮点击监听不设置默认点击关闭弹框
* @return
* @return 确定按钮点击监听
* @deprecated 请使用 {@link #getOnClickConfirm()}来代替后续版本可能会移除此方法
*/
public View.OnClickListener getOnClickOk() {
@ -485,8 +555,8 @@ public class BaseDialogConfig {
/**
* 设置确定按钮点击监听不设置默认点击关闭弹框
* @param onClickOk
* @return
* @param onClickOk 确定按钮点击监听
* @return {@link BaseDialogConfig}
* @deprecated 请使用 {@link #setOnClickConfirm(View.OnClickListener)}来代替后续版本可能会移除此方法
*/
public BaseDialogConfig setOnClickOk(View.OnClickListener onClickOk) {
@ -495,6 +565,7 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#x}
* @return {@link #x}
*/
public int getX() {
return x;
@ -502,6 +573,7 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#x}
* @param x x轴坐标
*/
public BaseDialogConfig setX(int x) {
this.x = x;
@ -510,6 +582,7 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#y}
* @return {@link #y}
*/
public int getY() {
return y;
@ -517,6 +590,8 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#y}
* @param y y轴坐标
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setY(int y) {
this.y = y;
@ -525,6 +600,7 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#verticalMargin}
* @return {@link #verticalMargin}
*/
public float getVerticalMargin() {
return verticalMargin;
@ -532,6 +608,7 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#verticalMargin}
* @param verticalMargin 垂直边距
*/
public void setVerticalMargin(float verticalMargin) {
this.verticalMargin = verticalMargin;
@ -539,6 +616,7 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#horizontalMargin}
* @return {@link #horizontalMargin}
*/
public float getHorizontalMargin() {
return horizontalMargin;
@ -546,6 +624,8 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#horizontalMargin}
* @param horizontalMargin 水平边距
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setHorizontalMargin(float horizontalMargin) {
this.horizontalMargin = horizontalMargin;
@ -554,6 +634,7 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#horizontalWeight}
* @return {@link #horizontalWeight}
*/
public float getHorizontalWeight() {
return horizontalWeight;
@ -561,6 +642,8 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#horizontalWeight}
* @param horizontalWeight 水平方向权重
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setHorizontalWeight(float horizontalWeight) {
this.horizontalWeight = horizontalWeight;
@ -568,6 +651,7 @@ public class BaseDialogConfig {
}
/**
* {@link WindowManager.LayoutParams#verticalWeight}
* @return {@link #verticalWeight}
*/
public float getVerticalWeight() {
return verticalWeight;
@ -575,6 +659,8 @@ public class BaseDialogConfig {
/**
* {@link WindowManager.LayoutParams#verticalWeight}
* @param verticalWeight 垂直方向权重
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setVerticalWeight(float verticalWeight) {
this.verticalWeight = verticalWeight;

@ -11,6 +11,7 @@ import com.king.app.dialog.BaseDialogConfig;
import com.king.app.dialog.R;
/**
* App对话框 Fragment封装便捷的对话框API使用时更简单
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class AppDialogFragment extends BaseDialogFragment {
@ -18,7 +19,6 @@ public class AppDialogFragment extends BaseDialogFragment {
private BaseDialogConfig config;
public static AppDialogFragment newInstance(BaseDialogConfig config) {
Bundle args = new Bundle();
AppDialogFragment fragment = new AppDialogFragment();
fragment.config = config;

@ -20,6 +20,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
/**
* 基础对话框 Fragment
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public abstract class BaseDialogFragment extends DialogFragment {
@ -48,6 +49,20 @@ public abstract class BaseDialogFragment extends DialogFragment {
return dialog;
}
/**
* 初始化对话框视图
* @param context 上下文
* @param dialog 对话框
* @param gravity 对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param horizontalMargin 水平方向边距
* @param verticalMargin 垂直方向边距
* @param horizontalWeight 水平方向权重
* @param verticalWeight 垂直方向权重
* @param animationStyleId 话框动画样式ID
*/
protected void initDialogWindow(Context context,Dialog dialog,int gravity,float widthRatio, int x, int y, float horizontalMargin, float verticalMargin, float horizontalWeight, float verticalWeight, int animationStyleId){
setDialogWindow(context, dialog, gravity, widthRatio, x, y, horizontalMargin, verticalMargin, horizontalWeight, verticalWeight, animationStyleId);
}
@ -82,6 +97,10 @@ public abstract class BaseDialogFragment extends DialogFragment {
window.setAttributes(lp);
}
/**
* 根视图
* @return
*/
protected View getRootView(){
return mRootView;
}
@ -103,8 +122,16 @@ public abstract class BaseDialogFragment extends DialogFragment {
}
};
/**
* 获取根布局ID
* @return 根布局ID
*/
public abstract int getRootLayoutId();
/**
* 初始化
* @param rootView
*/
public abstract void init(View rootView);
}

@ -2,11 +2,8 @@
package="com.king.app.updater">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application>
@ -15,7 +12,7 @@
<provider
android:name="com.king.app.updater.provider.AppUpdaterFileProvider"
android:authorities="${applicationId}.fileProvider"
android:authorities="${applicationId}.AppUpdaterFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

@ -96,7 +96,7 @@ public class AppUpdater {
startDownloadService();
}else{
throw new NullPointerException("Url = null");
throw new NullPointerException("Url must not be empty.");
}
}
@ -104,7 +104,6 @@ public class AppUpdater {
* 启动下载服务
*/
private void startDownloadService(){
Intent intent = new Intent(mContext, DownloadService.class);
if(mCallback!=null || mHttpManager!=null){//bindService
mServiceConnection = new ServiceConnection() {
@ -148,6 +147,7 @@ public class AppUpdater {
*/
public static class Builder{
private UpdateConfig mConfig;
public Builder(){
@ -269,7 +269,7 @@ public class AppUpdater {
/**
* 设置FileProvider的authority
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".fileProvider"}
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
* @return
*/
public Builder setAuthority(String authority){
@ -373,8 +373,7 @@ public class AppUpdater {
public AppUpdater build(@NonNull Context context){
AppUpdater appUpdater = new AppUpdater(context,mConfig);
return appUpdater;
return new AppUpdater(context,mConfig);
}
}

@ -18,7 +18,9 @@ import androidx.annotation.DrawableRes;
*/
public class UpdateConfig implements Parcelable {
/**
* APK下载的Url
*/
private String mUrl;
/**
* 保存路径
@ -56,7 +58,7 @@ public class UpdateConfig implements Parcelable {
*/
private String mChannelName;
/**
* 默认{@link Context#getPackageName() + ".fileProvider"}
* 默认{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
*/
private String mAuthority;
/**
@ -229,7 +231,7 @@ public class UpdateConfig implements Parcelable {
/**
* 设置FileProvider的authority
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".fileProvider"}
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
*/
public void setAuthority(String authority) {
this.mAuthority = authority;

@ -23,7 +23,7 @@ public final class Constants {
public static final int NONE = -1;
public static final String DEFAULT_FILE_PROVIDER = ".fileProvider";
public static final String DEFAULT_FILE_PROVIDER = ".AppUpdaterFileProvider";
public static final String DEFAULT_DIR = "apk";

@ -2,9 +2,8 @@ package com.king.app.updater.http;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import com.king.app.updater.constant.Constants;
import com.king.app.updater.util.LogUtils;
import com.king.app.updater.util.SSLSocketFactoryUtils;
import java.io.File;
@ -20,7 +19,7 @@ import javax.net.ssl.HttpsURLConnection;
import androidx.annotation.Nullable;
/**
* HttpManager使用{@link HttpURLConnection}实现{@link IHttpManager}
* HttpManager使用 {@link HttpURLConnection} 实现 {@link IHttpManager}
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class HttpManager implements IHttpManager {
@ -32,7 +31,7 @@ public class HttpManager implements IHttpManager {
private int mTimeout;
private boolean isCancel;
private DownloadTask mDownloadTask;
private static volatile HttpManager INSTANCE;
@ -61,19 +60,21 @@ public class HttpManager implements IHttpManager {
@Override
public void download(String url, String path, String filename, @Nullable Map<String,String> requestProperty, DownloadCallback callback) {
isCancel = false;
new DownloadTask(url,path,filename,requestProperty,callback).execute();
mDownloadTask = new DownloadTask(url, path, filename, mTimeout, requestProperty, callback);
mDownloadTask.execute();
}
@Override
public void cancel() {
isCancel = true;
if(mDownloadTask != null){
mDownloadTask.isCancel = true;
}
}
/**
* 异步下载任务
*/
private class DownloadTask extends AsyncTask<Void,Long,File> {
private static class DownloadTask extends AsyncTask<Void,Long,File> {
private String url;
@ -87,13 +88,17 @@ public class HttpManager implements IHttpManager {
private Exception exception;
public DownloadTask(String url, String path, String filename ,@Nullable Map<String,String> requestProperty, DownloadCallback callback){
private int timeout;
private volatile boolean isCancel;
public DownloadTask(String url, String path, String filename, int timeout, @Nullable Map<String,String> requestProperty, DownloadCallback callback){
this.url = url;
this.path = path;
this.filename = filename;
this.timeout = timeout;
this.callback = callback;
this.requestProperty = requestProperty;
}
private File download(String url) throws Exception{
@ -101,8 +106,8 @@ public class HttpManager implements IHttpManager {
connect.setRequestMethod("GET");
connect.setRequestProperty("Accept-Encoding", "identity");
connect.setReadTimeout(mTimeout);
connect.setConnectTimeout(mTimeout);
connect.setReadTimeout(timeout);
connect.setConnectTimeout(timeout);
if(requestProperty != null){
for(Map.Entry<String,String> entry : requestProperty.entrySet()){
@ -112,7 +117,7 @@ public class HttpManager implements IHttpManager {
connect.connect();
Log.d(Constants.TAG,"Content-Type:" + connect.getContentType());
LogUtils.d("Content-Type:" + connect.getContentType());
int responseCode = connect.getResponseCode();
switch (responseCode){
case HttpURLConnection.HTTP_OK: {
@ -124,7 +129,7 @@ public class HttpManager implements IHttpManager {
length = connect.getContentLengthLong();
}
Log.d(Constants.TAG, "contentLength:" + length);
LogUtils.d( "contentLength:" + length);
long progress = 0;
@ -140,7 +145,7 @@ public class HttpManager implements IHttpManager {
}
fos.write(buffer, 0, len);
progress += len;
//更新进度
// 更新进度
if (length > 0) {
publishProgress(progress, length);
}
@ -163,9 +168,9 @@ public class HttpManager implements IHttpManager {
case HttpURLConnection.HTTP_MOVED_TEMP:
case HttpURLConnection.HTTP_SEE_OTHER:
case HTTP_TEMP_REDIRECT:
case HTTP_PERM_REDIRECT: {//重定向
case HTTP_PERM_REDIRECT: {// 重定向
String redirectUrl = connect.getHeaderField("Location");
Log.d(Constants.TAG,"redirectUrl = " + redirectUrl);
LogUtils.d("redirectUrl = " + redirectUrl);
connect.disconnect();
return download(redirectUrl);
}
@ -230,7 +235,6 @@ public class HttpManager implements IHttpManager {
}
}
}
}

@ -8,7 +8,7 @@ import java.util.Map;
import androidx.annotation.Nullable;
/**
* IHttpManager 默认提供{@link HttpManager} {@link OkHttpManager}两种实现
* IHttpManager 默认提供 {@link HttpManager} {@link OkHttpManager} 两种实现
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface IHttpManager {

@ -1,9 +1,8 @@
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.LogUtils;
import com.king.app.updater.util.SSLSocketFactoryUtils;
import org.apache.http.conn.ssl.SSLSocketFactory;
@ -23,8 +22,8 @@ import okhttp3.Request;
import okhttp3.Response;
/**
* OkHttpManager使用{@link OkHttpClient}实现{@link IHttpManager}
* 使用OkHttpManager时必须依赖OkHttp库
* OkHttpManager使用 {@link OkHttpClient} 实现 {@link IHttpManager}
* <p>使用 OkHttpManager 时必须依赖 OkHttp
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class OkHttpManager implements IHttpManager {
@ -33,7 +32,7 @@ public class OkHttpManager implements IHttpManager {
private OkHttpClient okHttpClient;
private boolean isCancel;
private DownloadTask mDownloadTask;
private static volatile OkHttpManager INSTANCE;
@ -77,20 +76,23 @@ public class OkHttpManager implements IHttpManager {
@Override
public void download(String url,final String path,final String filename, @Nullable Map<String, String> requestProperty,final DownloadCallback callback) {
isCancel = false;
new DownloadTask(okHttpClient,url,path,filename,requestProperty,callback).execute();
mDownloadTask = new DownloadTask(okHttpClient, url, path, filename, requestProperty, callback);
mDownloadTask.execute();
}
@Override
public void cancel() {
isCancel = true;
if(mDownloadTask != null){
mDownloadTask.isCancel = true;
}
}
/**
* 异步下载任务
*/
private class DownloadTask extends AsyncTask<Void,Long,File> {
private static class DownloadTask extends AsyncTask<Void,Long,File> {
private String url;
private String path;
@ -105,6 +107,8 @@ public class OkHttpManager implements IHttpManager {
private OkHttpClient okHttpClient;
private volatile boolean isCancel;
public DownloadTask(OkHttpClient okHttpClient,String url, String path, String filename ,@Nullable Map<String,String> requestProperty, DownloadCallback callback){
this.okHttpClient = okHttpClient;
this.url = url;
@ -138,7 +142,7 @@ public class OkHttpManager implements IHttpManager {
long length = response.body().contentLength();
Log.d(Constants.TAG,"contentLength:" + length);
LogUtils.d("contentLength:" + length);
long progress = 0;
@ -157,7 +161,7 @@ public class OkHttpManager implements IHttpManager {
}
fos.write(buffer,0,len);
progress += len;
//更新进度
// 更新进度
if(length > 0){
publishProgress(progress,length);
}
@ -175,7 +179,7 @@ public class OkHttpManager implements IHttpManager {
return file;
}else {//连接失败
}else {// 连接失败
throw new ConnectException(String.format("responseCode = %d",response.code()));
}

@ -0,0 +1,25 @@
package com.king.app.updater.notify;
import android.content.Context;
import com.king.app.updater.UpdateConfig;
import java.io.File;
import androidx.annotation.DrawableRes;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface INotification {
void onStart(Context context, int notifyId, String channelId, String channelName, @DrawableRes int icon, CharSequence title, CharSequence content, boolean isVibrate, boolean isSound, boolean isCancelDownload);
void onProgress(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, int progress, int size, boolean isCancelDownload);
void onFinish(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, File file, String authority);
void onError(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, boolean isReDownload, UpdateConfig config);
void onCancel(Context context, int notifyId);
}

@ -0,0 +1,38 @@
package com.king.app.updater.notify;
import android.content.Context;
import com.king.app.updater.UpdateConfig;
import com.king.app.updater.util.NotificationUtils;
import java.io.File;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class NotificationImpl implements INotification {
@Override
public void onStart(Context context, int notifyId, String channelId, String channelName, int icon, CharSequence title, CharSequence content, boolean isVibrate, boolean isSound, boolean isCancelDownload) {
NotificationUtils.showStartNotification(context, notifyId, channelId, channelName, icon, title, content, isVibrate, isSound, isCancelDownload);
}
@Override
public void onProgress(Context context, int notifyId, String channelId, int icon, CharSequence title, CharSequence content, int progress, int size, boolean isCancelDownload) {
NotificationUtils.showProgressNotification(context, notifyId, channelId, icon, title, content, progress, size, isCancelDownload);
}
@Override
public void onFinish(Context context, int notifyId, String channelId, int icon, CharSequence title, CharSequence content, File file, String authority) {
NotificationUtils.showFinishNotification(context, notifyId, channelId, icon, title, content, file, authority);
}
@Override
public void onError(Context context, int notifyId, String channelId, int icon, CharSequence title, CharSequence content, boolean isReDownload, UpdateConfig config) {
NotificationUtils.showErrorNotification(context, notifyId, channelId, icon, title, content, isReDownload, config);
}
@Override
public void onCancel(Context context, int notifyId) {
NotificationUtils.cancelNotification(context, notifyId);
}
}

@ -7,7 +7,6 @@ import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import com.king.app.updater.R;
import com.king.app.updater.UpdateConfig;
@ -15,16 +14,22 @@ 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.notify.INotification;
import com.king.app.updater.notify.NotificationImpl;
import com.king.app.updater.util.AppUtils;
import com.king.app.updater.util.NotificationUtils;
import com.king.app.updater.util.LogUtils;
import java.io.File;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
/**
* 下载服务
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class DownloadService extends Service {
@ -34,22 +39,16 @@ public class DownloadService extends Service {
* 是否在下载防止重复下载
*/
private boolean isDownloading;
/**
* 最后更新进度用来降频刷新
*/
private int mLastProgress = -1;
/**
* 最后进度更新时间用来降频刷新
*/
private long mLastTime;
/**
* 失败后重新下载次数
*/
private int mCount = 0;
/**
* Http管理器
*/
private IHttpManager mHttpManager;
private File mFile;
private File mApkFile;
private Context getContext(){
return this;
@ -57,9 +56,7 @@ public class DownloadService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null){
boolean isStop = intent.getBooleanExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,false);
if(isStop){
stopDownload();
@ -71,9 +68,9 @@ public class DownloadService extends Service {
}
//获取配置信息
UpdateConfig config = intent.getParcelableExtra(Constants.KEY_UPDATE_CONFIG);
startDownload(config,null,null);
startDownload(config,null,null, new NotificationImpl());
}else{
Log.w(Constants.TAG,"Please do not duplicate downloads.");
LogUtils.w("Please do not duplicate downloads.");
}
}
@ -90,18 +87,13 @@ public class DownloadService extends Service {
* @param httpManager
* @param callback
*/
public void startDownload(UpdateConfig config,IHttpManager httpManager,UpdateCallback callback){
if(config == null){
return;
}
public void startDownload(@NonNull UpdateConfig config,@Nullable IHttpManager httpManager,@Nullable UpdateCallback callback, @Nullable INotification notification){
if(callback != null){
callback.onDownloading(isDownloading);
}
if(isDownloading){
Log.w(Constants.TAG,"Please do not duplicate downloads.");
LogUtils.w("Please do not duplicate downloads.");
return;
}
@ -111,7 +103,7 @@ public class DownloadService extends Service {
//如果保存路径为空则使用缓存路径
if(TextUtils.isEmpty(path)){
path = getExternalFilesDir(getContext());
path = getCacheFilesDir(getContext());
}
File dirFile = new File(path);
if(!dirFile.exists()){
@ -123,49 +115,45 @@ public class DownloadService extends Service {
filename = AppUtils.getAppFullName(getContext(),url,getResources().getString(R.string.app_name));
}
mFile = new File(path,filename);
if(mFile.exists()){//文件是否存在
mApkFile = new File(path, filename);
if(mApkFile.exists()){//文件是否存在
Integer versionCode = config.getVersionCode();
String apkMD5 = config.getApkMD5();
//是否存在相同的apk
boolean isExistApk = false;
if(!TextUtils.isEmpty(apkMD5)){//如果存在MD5,则优先校验MD5
Log.d(Constants.TAG,String.format("UpdateConfig.apkMD5:%s",apkMD5));
isExistApk = AppUtils.checkFileMD5(mFile,apkMD5);
LogUtils.d(String.format(Locale.getDefault(),"UpdateConfig.apkMD5:%s",apkMD5));
isExistApk = AppUtils.checkFileMD5(mApkFile,apkMD5);
}else if(versionCode != null){//如果存在versionCode,则校验versionCode
try{
Log.d(Constants.TAG,String.format("UpdateConfig.versionCode:%d",versionCode));
isExistApk = AppUtils.apkExists(getContext(),versionCode,mFile);
}catch (Exception e){
Log.w(Constants.TAG,e);
}
LogUtils.d(String.format(Locale.getDefault(),"UpdateConfig.versionCode:%d",versionCode));
isExistApk = AppUtils.apkExists(getContext(),versionCode,mApkFile);
}
if(isExistApk){
//本地已经存在要下载的APK
Log.d(Constants.TAG,"CacheFile:" + mFile);
LogUtils.d("CacheFile:" + mApkFile);
if(config.isInstallApk()){
String authority = config.getAuthority();
if(TextUtils.isEmpty(authority)){//如果为空则默认
authority = getContext().getPackageName() + Constants.DEFAULT_FILE_PROVIDER;
authority = AppUtils.getFileProviderAuthority(getContext());
}
AppUtils.installApk(getContext(),mFile,authority);
AppUtils.installApk(getContext(), mApkFile, authority);
}
if(callback!=null){
callback.onFinish(mFile);
if(callback != null){
callback.onFinish(mApkFile);
}
stopService();
return;
}
//删除旧文件
mFile.delete();
mApkFile.delete();
}
Log.d(Constants.TAG,"File:" + mFile);
LogUtils.d("File:" + mApkFile);
mHttpManager = httpManager != null ? httpManager : HttpManager.getInstance();
mHttpManager.download(url,path,filename,config.getRequestProperty(),new AppDownloadCallback(config,callback));
IHttpManager.DownloadCallback downloadCallback = new AppDownloadCallback(getContext(),this, config, mApkFile, callback, notification);
mHttpManager.download(url,path,filename,config.getRequestProperty(), downloadCallback);
}
@ -183,12 +171,18 @@ public class DownloadService extends Service {
* @param context
* @return
*/
private String getExternalFilesDir(Context context) {
File[] files = ContextCompat.getExternalFilesDirs(context,Constants.DEFAULT_DIR);
if(files!=null && files.length > 0){
private String getCacheFilesDir(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();
File externalFilesDir = context.getExternalFilesDir(Constants.DEFAULT_DIR);
if(externalFilesDir != null){
return externalFilesDir.getAbsolutePath();
}
return new File(context.getFilesDir(), Constants.DEFAULT_DIR).getAbsolutePath();
}
@ -203,7 +197,14 @@ public class DownloadService extends Service {
//---------------------------------------- DownloadCallback
public class AppDownloadCallback implements IHttpManager.DownloadCallback{
/**
* App下载回调接口
*/
public static class AppDownloadCallback implements IHttpManager.DownloadCallback {
private Context context;
private DownloadService downloadService;
public UpdateConfig config;
@ -231,22 +232,38 @@ public class DownloadService extends Service {
private UpdateCallback callback;
private int reDownloads;
private INotification notification;
/**
* 最后更新进度用来降频刷新
*/
private int lastProgress;
/**
* 最后进度更新时间用来降频刷新
*/
private long lastTime;
/**
* APK文件
*/
private File apkFile;
private AppDownloadCallback(UpdateConfig config,UpdateCallback callback){
private AppDownloadCallback(Context context, DownloadService downloadService, UpdateConfig config, File apkFile, UpdateCallback callback, INotification notification){
this.context = context;
this.downloadService = downloadService;
this.config = config;
this.apkFile = apkFile;
this.callback = callback;
this.notification = notification;
this.isShowNotification = config.isShowNotification();
this.notifyId = config.getNotificationId();
this.reDownloads = config.getReDownloads();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
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.getAppIcon(getContext());
this.notificationIcon = AppUtils.getAppIcon(context);
}else{
this.notificationIcon = config.getNotificationIcon();
}
@ -255,23 +272,25 @@ public class DownloadService extends Service {
this.authority = config.getAuthority();
if(TextUtils.isEmpty(config.getAuthority())){//如果为空则默认
authority = getContext().getPackageName() + Constants.DEFAULT_FILE_PROVIDER;
authority = AppUtils.getFileProviderAuthority(context);
}
this.isShowPercentage = config.isShowPercentage();
this.isReDownload = config.isReDownload();
this.isDeleteCancelFile = config.isDeleteCancelFile();
this.isCancelDownload = config.isCancelDownload();
//支持下载失败时重新下载,当重新下载次数不超过限制时才被允许
this.isReDownload = config.isReDownload() && downloadService.mCount < config.getReDownloads();
}
@Override
public void onStart(String url) {
Log.d(Constants.TAG,"onStart:" + url);
isDownloading = true;
mLastProgress = 0;
if(isShowNotification){
NotificationUtils.showStartNotification(getContext(),notifyId,channelId,channelName,notificationIcon,getString(R.string.app_updater_start_notification_title),getString(R.string.app_updater_start_notification_content),config.isVibrate(),config.isSound(),isCancelDownload);
LogUtils.i("url:" + url);
downloadService.isDownloading = true;
lastProgress = 0;
if(isShowNotification && notification != null){
notification.onStart(context,notifyId,channelId,channelName,notificationIcon,getString(R.string.app_updater_start_notification_title),getString(R.string.app_updater_start_notification_content),config.isVibrate(),config.isSound(),isCancelDownload);
}
if(callback != null){
@ -281,25 +300,24 @@ public class DownloadService extends Service {
@Override
public void onProgress(long progress, long total) {
boolean isChange = false;
long curTime = System.currentTimeMillis();
if(mLastTime + 200 < curTime) {//降低更新频率
mLastTime = curTime;
if(lastTime + 200 < curTime || progress == total) {//降低更新频率
lastTime = curTime;
int currProgress = Math.round(progress * 1.0f / total * 100.0f);
if(currProgress != mLastProgress){//百分比改变了才更新
if(currProgress != lastProgress){//百分比改变了才更新
isChange = true;
mLastProgress = currProgress;
lastProgress = currProgress;
String percentage = currProgress + "%";
if(isShowNotification) {
String content = getString(R.string.app_updater_progress_notification_content);
LogUtils.i(String.format(Locale.getDefault(),"%s \t(%d/%d)", percentage, progress, total));
if(isShowNotification && notification != null) {
String content = context.getString(R.string.app_updater_progress_notification_content);
if (isShowPercentage) {
content += percentage;
}
NotificationUtils.showProgressNotification(getContext(),notifyId, channelId, notificationIcon, getString(R.string.app_updater_progress_notification_title), content, currProgress, 100,isCancelDownload);
notification.onProgress(context,notifyId, channelId, notificationIcon, context.getString(R.string.app_updater_progress_notification_title), content, currProgress, 100,isCancelDownload);
}
}
}
@ -311,50 +329,56 @@ public class DownloadService extends Service {
@Override
public void onFinish(File file) {
Log.d(Constants.TAG,"onFinish:" + file);
isDownloading = false;
NotificationUtils.showFinishNotification(getContext(),notifyId,channelId,notificationIcon,getString(R.string.app_updater_finish_notification_title),getString(R.string.app_updater_finish_notification_content),file,authority);
LogUtils.d("File:" + file);
downloadService.isDownloading = false;
if(isShowNotification && notification != null){
notification.onFinish(context,notifyId,channelId,notificationIcon,getString(R.string.app_updater_finish_notification_title),getString(R.string.app_updater_finish_notification_content),file,authority);
}
if(isInstallApk){
AppUtils.installApk(getContext(),file,authority);
AppUtils.installApk(context,file,authority);
}
if(callback != null){
callback.onFinish(file);
}
stopService();
downloadService.stopService();
}
@Override
public void onError(Exception e) {
Log.w(Constants.TAG,"onError:"+ e.getMessage());
isDownloading = false;
if(isShowNotification){
//支持下载失败时重新下载,当重新下载次数不超过限制时才被允许
boolean isReDownload = this.isReDownload && mCount < reDownloads;
LogUtils.w(e.getMessage());
downloadService.isDownloading = false;
if(isShowNotification && notification != null){
String content = isReDownload ? getString(R.string.app_updater_error_notification_content_re_download) : getString(R.string.app_updater_error_notification_content);
NotificationUtils.showErrorNotification(getContext(),notifyId,channelId,notificationIcon,getString(R.string.app_updater_error_notification_title),content,isReDownload,config);
notification.onError(context,notifyId,channelId,notificationIcon,getString(R.string.app_updater_error_notification_title),content,isReDownload,config);
}
if(callback != null){
callback.onError(e);
}
if(!isReDownload){
stopService();
downloadService.stopService();
}
}
@Override
public void onCancel() {
Log.d(Constants.TAG,"onCancel");
isDownloading = false;
NotificationUtils.cancelNotification(getContext(),notifyId);
LogUtils.d("Cancel download.");
downloadService.isDownloading = false;
if(isShowNotification && notification != null){
notification.onCancel(context,notifyId);
}
if(callback != null){
callback.onCancel();
}
if(isDeleteCancelFile && mFile!=null){
mFile.delete();
if(isDeleteCancelFile && apkFile != null){
apkFile.delete();
}
stopService();
downloadService.stopService();
}
private String getString(@StringRes int resId){
return context.getString(resId);
}
}
@ -374,22 +398,47 @@ public class DownloadService extends Service {
}
/**
* 提供绑定服务的方式下载
* 提供绑定服务的方式进行下载
*/
public class DownloadBinder extends Binder {
public void start(UpdateConfig config){
/**
* 开始下载
* @param config {@link UpdateConfig}
*/
public void start(@NonNull UpdateConfig config){
start(config,null);
}
public void start(UpdateConfig config,UpdateCallback callback){
start(config,null,callback);
/**
* 开始下载
* @param config {@link UpdateConfig}
* @param callback {@link UpdateCallback}
*/
public void start(@NonNull UpdateConfig config, @Nullable UpdateCallback callback){
start(config,null, callback);
}
public void start(UpdateConfig config,IHttpManager httpManager,UpdateCallback callback){
startDownload(config,httpManager,callback);
/**
* 开始下载
* @param config {@link UpdateConfig}
* @param httpManager {@link IHttpManager}
* @param callback {@link UpdateCallback}
*/
public void start(@NonNull UpdateConfig config, @Nullable IHttpManager httpManager, @Nullable UpdateCallback callback){
start(config, httpManager, callback, new NotificationImpl());
}
/**
* 开始下载
* @param config {@link UpdateConfig}
* @param httpManager {@link IHttpManager}
* @param callback {@link UpdateCallback}
* @param notification {@link INotification}
*/
public void start(@NonNull UpdateConfig config, @Nullable IHttpManager httpManager, @Nullable UpdateCallback callback,@NonNull INotification notification){
startDownload(config, httpManager, callback, notification);
}
}

@ -47,7 +47,7 @@ public final class AppUtils {
}
String filename = getAppName(context);
Log.d(Constants.TAG, "AppName:" + filename);
if(TextUtils.isEmpty(filename)){
filename = defaultName;
}
@ -65,7 +65,7 @@ public final class AppUtils {
*/
public static PackageInfo getPackageInfo(Context context) throws PackageManager.NameNotFoundException {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo( context.getPackageName(), 0);
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
return packageInfo;
}
@ -75,7 +75,7 @@ public final class AppUtils {
* @param archiveFilePath
* @return
*/
public static PackageInfo getPackageInfo(Context context, String archiveFilePath) throws Exception {
public static PackageInfo getPackageInfo(Context context, String archiveFilePath) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(archiveFilePath, PackageManager.GET_ACTIVITIES);
return packageInfo;
@ -106,7 +106,6 @@ public final class AppUtils {
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
@ -128,7 +127,6 @@ public final class AppUtils {
* @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);
intent.addCategory(Intent.CATEGORY_DEFAULT);
@ -152,11 +150,11 @@ public final class AppUtils {
* @return
* @throws Exception
*/
public static boolean apkExists(Context context,int versionCode,File file) throws Exception{
if(file!=null && file.exists()){
public static boolean apkExists(Context context,int versionCode,File file){
if(file != null && file.exists()){
String packageName = context.getPackageName();
PackageInfo packageInfo = AppUtils.getPackageInfo(context,file.getAbsolutePath());
if(packageInfo != null){//比对versionCode
if(packageInfo != null){// 比对versionCode
Log.d(Constants.TAG,String.format("ApkVersionCode:%d",packageInfo.versionCode));
if(versionCode == packageInfo.versionCode){
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
@ -244,6 +242,10 @@ public final class AppUtils {
return null;
}
public static String getFileProviderAuthority(Context context){
return context.getPackageName() + Constants.DEFAULT_FILE_PROVIDER;
}
/**
* 关闭
* @param descriptor

@ -0,0 +1,316 @@
/*
Copyright © 2015, 2016 Jenly Yu <a href="mailto:jenly1314@gmail.com">Jenly</a>
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.king.app.updater.util;
import android.util.Log;
import java.util.Locale;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class LogUtils {
public static final String TAG = "AppUpdater";
public static final String VERTICAL = "|";
/** 是否显示Log日志 */
private static boolean isShowLog = true;
/** Log日志优先权 */
private static int priority = 1;
/**
* Priority constant for the println method;use System.out.println
*/
public static final int PRINTLN = 1;
/**
* Priority constant for the println method; use Log.v.
*/
public static final int VERBOSE = 2;
/**
* Priority constant for the println method; use Log.d.
*/
public static final int DEBUG = 3;
/**
* Priority constant for the println method; use Log.i.
*/
public static final int INFO = 4;
/**
* Priority constant for the println method; use Log.w.
*/
public static final int WARN = 5;
/**
* Priority constant for the println method; use Log.e.
*/
public static final int ERROR = 6;
/**
* Priority constant for the println method.use Log.wtf.
*/
public static final int ASSERT = 7;
public static final String TAG_FORMAT = "%s.%s(%s:%d)";
private LogUtils(){
throw new AssertionError();
}
public static void setShowLog(boolean isShowLog) {
LogUtils.isShowLog = isShowLog;
}
public static boolean isShowLog() {
return isShowLog;
}
public static int getPriority() {
return priority;
}
public static void setPriority(int priority) {
LogUtils.priority = priority;
}
/**
* 根据堆栈生成TAG
* @return TAG|className.methodName(fileName:lineNumber)
*/
private static String generateTag(StackTraceElement caller) {
String tag = TAG_FORMAT;
String callerClazzName = caller.getClassName();
callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
tag = String.format(Locale.getDefault(),tag,callerClazzName, caller.getMethodName(),caller.getFileName(),caller.getLineNumber());
return new StringBuilder().append(TAG).append(VERTICAL).append(tag).toString();
}
/**
* 获取堆栈
* @param n
* n=0 VMStack
* n=1 Thread
* n=3 CurrentStack
* n=4 CallerStack
* ...
* @return
*/
public static StackTraceElement getStackTraceElement(int n) {
return Thread.currentThread().getStackTrace()[n];
}
/**
* 获取调用方的堆栈TAG
* @return
*/
private static String getCallerStackLogTag(){
return generateTag(getStackTraceElement(5));
}
/**
*
* @param t
* @return
*/
private static String getStackTraceString(Throwable t){
return Log.getStackTraceString(t);
}
// -----------------------------------Log.v
/**
* Log.v
* @param msg
*/
public static void v(String msg) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), String.valueOf(msg));
}
public static void v(Throwable t) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), getStackTraceString(t));
}
public static void v(String msg,Throwable t) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.d
/**
* Log.d
* @param msg
*/
public static void d(String msg) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), String.valueOf(msg));
}
public static void d(Throwable t) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), getStackTraceString(t));
}
public static void d(String msg,Throwable t) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.i
/**
* Log.i
* @param msg
*/
public static void i(String msg) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), String.valueOf(msg));
}
public static void i(Throwable t) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), getStackTraceString(t));
}
public static void i(String msg,Throwable t) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.w
/**
* Log.w
* @param msg
*/
public static void w(String msg) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), String.valueOf(msg));
}
public static void w(Throwable t) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), getStackTraceString(t));
}
public static void w(String msg,Throwable t) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.e
/**
* Log.e
* @param msg
*/
public static void e(String msg) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), String.valueOf(msg));
}
public static void e(Throwable t) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), getStackTraceString(t));
}
public static void e(String msg,Throwable t) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.wtf
/**
* Log.wtf
* @param msg
*/
public static void wtf(String msg) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), String.valueOf(msg));
}
public static void wtf(Throwable t) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), getStackTraceString(t));
}
public static void wtf(String msg,Throwable t) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------System.out.print
/**
* System.out.print
*
* @param msg
*/
public static void print(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.print(msg);
}
public static void print(Object obj) {
if (isShowLog && priority <= PRINTLN)
System.out.print(obj);
}
// -----------------------------------System.out.printf
/**
* System.out.printf
*
* @param msg
*/
public static void printf(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.printf(msg);
}
// -----------------------------------System.out.println
/**
* System.out.println
*
* @param msg
*/
public static void println(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.println(msg);
}
public static void println(Object obj) {
if (isShowLog && priority <= PRINTLN)
System.out.println(obj);
}
}

@ -53,7 +53,7 @@ public class NotificationUtils {
if(isCancelDownload){
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,true);
PendingIntent deleteIntent = PendingIntent.getService(context, notifyId,intent, PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent deleteIntent = PendingIntent.getService(context, notifyId,intent, getPendingIntentFlags(PendingIntent.FLAG_CANCEL_CURRENT));
builder.setDeleteIntent(deleteIntent);
}
@ -83,7 +83,7 @@ public class NotificationUtils {
if(isCancelDownload){
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,true);
PendingIntent deleteIntent = PendingIntent.getService(context, notifyId,intent, PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent deleteIntent = PendingIntent.getService(context, notifyId, intent, getPendingIntentFlags(PendingIntent.FLAG_CANCEL_CURRENT));
builder.setDeleteIntent(deleteIntent);
}
@ -112,7 +112,7 @@ public class NotificationUtils {
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setAutoCancel(true);
Intent intent = AppUtils.getInstallIntent(context,file,authority);
PendingIntent clickIntent = PendingIntent.getActivity(context, notifyId,intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent clickIntent = PendingIntent.getActivity(context, notifyId, intent, getPendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT));
builder.setContentIntent(clickIntent);
Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
@ -133,14 +133,15 @@ public class NotificationUtils {
public static void showErrorNotification(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, boolean isReDownload, UpdateConfig config){
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setAutoCancel(true);
if(isReDownload){//重新下载
int flag = getPendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT);
if(isReDownload){// 重新下载
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_RE_DOWNLOAD,true);
intent.putExtra(Constants.KEY_UPDATE_CONFIG,config);
PendingIntent clickIntent = PendingIntent.getService(context, notifyId,intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent clickIntent = PendingIntent.getService(context, notifyId,intent, flag);
builder.setContentIntent(clickIntent);
}else{
PendingIntent clickIntent = PendingIntent.getService(context, notifyId,new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent clickIntent = PendingIntent.getService(context, notifyId, new Intent(), flag);
builder.setContentIntent(clickIntent);
}
@ -229,7 +230,7 @@ public class NotificationUtils {
builder.setContentText(content);
builder.setOngoing(true);
if(progress!= Constants.NONE && size!=Constants.NONE){
if(progress != Constants.NONE && size != Constants.NONE){
builder.setProgress(size,progress,false);
}
@ -245,4 +246,15 @@ public class NotificationUtils {
getNotificationManager(context).notify(id,notification);
}
/**
* 获取 PendingIntent flags
* @param flag
* @return
*/
private static int getPendingIntentFlags(int flag){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return flag | PendingIntent.FLAG_IMMUTABLE;
}
return flag;
}
}

@ -20,51 +20,49 @@ import androidx.core.app.ActivityCompat;
*/
public final class PermissionUtils {
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE};
private PermissionUtils(){
private PermissionUtils() {
throw new AssertionError();
}
/**
* 校验权限
*
* @param activity
* @param requestCode
* @return
*/
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);
int readPhoneState = checkPermission(activity,Manifest.permission.READ_PHONE_STATE);
if(readResult != PackageManager.PERMISSION_GRANTED || writeResult != PackageManager.PERMISSION_GRANTED || readPhoneState != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(activity,PERMISSIONS_STORAGE,requestCode);
public static boolean verifyReadAndWritePermissions(@NonNull Activity activity, int requestCode) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
int readResult = checkPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
int writeResult = checkPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (readResult != PackageManager.PERMISSION_GRANTED || writeResult != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
return false;
}
}
return true;
}
public static int checkPermission(@NonNull Activity activity,@NonNull String permission){
return ActivityCompat.checkSelfPermission(activity,permission);
public static int checkPermission(@NonNull Activity activity, @NonNull String permission) {
return ActivityCompat.checkSelfPermission(activity, permission);
}
/**
* 获取通知权限
*
* @param context
*/
public static boolean isNotificationEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager.getImportance() == NotificationManager.IMPORTANCE_NONE) {
return false;
}
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
String CHECK_OP_NO_THROW = "checkOpNoThrow";
String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";

@ -1,17 +1,18 @@
package com.king.app.updater.util;
import android.content.Context;
import android.text.TextUtils;
import androidx.annotation.RawRes;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@ -27,7 +28,9 @@ import javax.net.ssl.X509TrustManager;
*/
public final class SSLSocketFactoryUtils {
private SSLSocketFactoryUtils(){
private static final String[] VERIFY_HOST_NAME = new String[]{};
private SSLSocketFactoryUtils() {
throw new AssertionError();
}
@ -35,7 +38,7 @@ public final class SSLSocketFactoryUtils {
SSLSocketFactory sslSocketFactory = null;
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{createTrustAllManager()}, new SecureRandom());
sslContext.init(null, getTrustAllManager(), new SecureRandom());
sslSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
@ -67,66 +70,55 @@ public final class SSLSocketFactoryUtils {
return tm;
}
public static TrustAllHostnameVerifier createTrustAllHostnameVerifier(){
public static TrustAllHostnameVerifier createTrustAllHostnameVerifier() {
return new TrustAllHostnameVerifier();
}
public static class TrustAllHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
if (TextUtils.isEmpty(hostname)) {
return false;
}
return !Arrays.asList(VERIFY_HOST_NAME).contains(hostname);
}
}
/**
*
* @param context
* @param keyServerStoreID
* @return
*/
public static SSLSocketFactory createSSLSocketFactory(Context context,@RawRes int keyServerStoreID){
public static SSLSocketFactory createSSLSocketFactory(Context context, @RawRes int keyServerStoreID) {
InputStream trustStream = context.getResources().openRawResource(keyServerStoreID);
return createSSLSocketFactory(trustStream);
}
/**
*
* @param certificates
* @return
*/
public static SSLSocketFactory createSSLSocketFactory(InputStream... certificates) {
SSLSocketFactory mSSLSocketFactory = null;
if(mSSLSocketFactory==null){
SSLSocketFactory sSLSocketFactory = null;
if (sSLSocketFactory == null) {
synchronized (SSLSocketFactoryUtils.class) {
if(mSSLSocketFactory==null){
SSLContext sslContext = null;
if (sSLSocketFactory == null) {
try {
sslContext = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
//获得服务器端证书
TrustManager[] turstManager = getTrustManager(certificates);
//初始化ssl证书库
try {
sslContext.init(null,turstManager,new SecureRandom());
} catch (KeyManagementException e) {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, getTrustManager(certificates), new SecureRandom());
sSLSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
//获得sslSocketFactory
mSSLSocketFactory = sslContext.getSocketFactory();
}
}
}
return mSSLSocketFactory;
return sSLSocketFactory;
}
/**
* 获得指定流中的服务器端证书库
*
* @param certificates
* @return
*/
@ -134,7 +126,7 @@ public final class SSLSocketFactoryUtils {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null,null);
keyStore.load(null, null);
int index = 0;
for (InputStream certificate : certificates) {
if (certificate == null) {
@ -143,12 +135,12 @@ public final class SSLSocketFactoryUtils {
Certificate certificate1;
try {
certificate1 = certificateFactory.generateCertificate(certificate);
}finally {
} finally {
certificate.close();
}
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias,certificate1);
keyStore.setCertificateEntry(certificateAlias, certificate1);
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory
@ -167,9 +159,9 @@ public final class SSLSocketFactoryUtils {
/**
* 获得信任所有服务器端证书库
* */
*/
public static TrustManager[] getTrustAllManager() {
return new X509TrustManager[] {createTrustAllManager()};
return new TrustManager[]{createTrustAllManager()};
}

Binary file not shown.

@ -10,8 +10,8 @@
{
"type": "SINGLE",
"filters": [],
"versionCode": 21,
"versionName": "1.1.2",
"versionCode": 22,
"versionName": "1.1.3",
"outputFile": "app-release.apk"
}
]

@ -2,6 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.king.appupdater">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"

@ -4,7 +4,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@ -18,9 +17,7 @@ import com.king.app.updater.AppUpdater;
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;
@ -32,11 +29,9 @@ import androidx.appcompat.app.AppCompatActivity;
*/
public class MainActivity extends AppCompatActivity {
private static String TAG = MainActivity.class.getSimpleName();
private final Object mLock = new Object();
//下载出现Failed to connect to raw.githubusercontent.com时,可以换个下载链接测试,github的raw.githubusercontent.com目前不太稳定。
// 下载出现Failed to connect to raw.githubusercontent.com时,可以换个下载链接测试,github的raw.githubusercontent.com目前不太稳定。
// private String mUrl = "https://raw.githubusercontent.com/jenly1314/AppUpdater/master/app/release/app-release.apk";
private String mUrl = "https://gitlab.com/jenly1314/AppUpdater/-/raw/master/app/release/app-release.apk";
@ -55,7 +50,7 @@ public class MainActivity extends AppCompatActivity {
// progressBar.setVisibility(View.INVISIBLE);
// progressBar.setMax(100);
PermissionUtils.verifyReadAndWritePermissions(this,Constants.RE_CODE_STORAGE_PERMISSION);
// PermissionUtils.verifyReadAndWritePermissions(this,Constants.RE_CODE_STORAGE_PERMISSION);
}
public Context getContext(){
@ -147,7 +142,6 @@ public class MainActivity extends AppCompatActivity {
int currProgress = (int)(progress * 1.0f / total * 100.0f);
tvProgress.setText(getString(R.string.app_updater_progress_notification_content) + currProgress + "%");
progressBar.setProgress(currProgress);
Log.d(TAG,String.format("onProgress:%d/%d | %d%%",progress,total,currProgress));
}else{
tvProgress.setText(getString(R.string.app_updater_start_notification_content));
}
@ -171,7 +165,7 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onStart(String url) {
super.onStart(url);
//模仿系统自带的横幅通知效果
// 模仿系统自带的横幅通知效果
AppDialogConfig config = new AppDialogConfig(getContext(),R.layout.dialog_heads_up);
config.setStyleId(R.style.app_dialog_heads_up)
.setWidthRatio(.95f)
@ -193,7 +187,7 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onProgress(long progress, long total, boolean isChange) {
Log.d(TAG,String.format("onProgress:%d/%d",progress,total));
}
@Override
@ -271,8 +265,8 @@ public class MainActivity extends AppCompatActivity {
public void onClick(View v) {
mAppUpdater = new AppUpdater.Builder()
.setUrl(mUrl)
// .setApkMD5("3df5b1c1d2bbd01b4a7ddb3f2722ccca")//支持MD5校验,如果缓存APK的MD5与此MD5相同,则直接取本地缓存安装,推荐使用MD5校验的方式
.setVersionCode(BuildConfig.VERSION_CODE)//支持versionCode校验,设置versionCode之后,新版本versionCode相同的apk只下载一次,优先取本地缓存,推荐使用MD5校验的方式
// .setApkMD5("3df5b1c1d2bbd01b4a7ddb3f2722ccca")// 支持MD5校验,如果缓存APK的MD5与此MD5相同,则直接取本地缓存安装,推荐使用MD5校验的方式
.setVersionCode(BuildConfig.VERSION_CODE)// 支持versionCode校验,设置versionCode之后,新版本versionCode相同的apk只下载一次,优先取本地缓存,推荐使用MD5校验的方式
.setFilename("AppUpdater.apk")
.setVibrate(true)
.build(getContext());

@ -15,8 +15,8 @@ android.useAndroidX=true
android.enableJetifier=true
VERSION_NAME=1.1.2
VERSION_CODE=21
VERSION_NAME=1.1.3
VERSION_CODE=22
GROUP=com.github.jenly1314.AppUpdater
POM_DESCRIPTION=AppUpdater for Android

@ -1,7 +1,7 @@
//App
def app_version = [:]
app_version.versionCode = 21
app_version.versionName = "1.1.2"
app_version.versionCode = 22
app_version.versionName = "1.1.3"
ext.app_version = app_version
//build version

Loading…
Cancel
Save