diff --git a/README.md b/README.md index a13ecb3..f13b02e 100644 --- a/README.md +++ b/README.md @@ -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相关的配置 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 dcb65bb..72c631c 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 @@ -16,6 +16,7 @@ import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentManager; /** + * App对话框:封装便捷的对话框API,使用时更简单 * @author Jenly */ 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 是否可取消(默认为true,false则拦截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 是否可取消(默认为true,false则拦截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 是否可取消(默认为true,false则拦截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 是否可取消(默认为true,false则拦截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 是否可取消(默认为true,false则拦截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 是否可取消(默认为true,false则拦截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(); diff --git a/app-dialog/src/main/java/com/king/app/dialog/AppDialogConfig.java b/app-dialog/src/main/java/com/king/app/dialog/AppDialogConfig.java index 7a60634..023bf05 100644 --- a/app-dialog/src/main/java/com/king/app/dialog/AppDialogConfig.java +++ b/app-dialog/src/main/java/com/king/app/dialog/AppDialogConfig.java @@ -29,6 +29,7 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; /** + * App 对话框配置 * @author Jenly */ 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 对应的视图类 + * @return 视图ID对应的视图 + */ private T findView(@IdRes int id){ return getDialogView().findViewById(id); } + /** + * 根据视图ID获取对应的视图 + * @param id 视图ID + * @param 对应的视图类 + * @return 视图ID对应的视图 + */ public 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); } diff --git a/app-dialog/src/main/java/com/king/app/dialog/BaseDialogConfig.java b/app-dialog/src/main/java/com/king/app/dialog/BaseDialogConfig.java index da3f22d..0635a3e 100644 --- a/app-dialog/src/main/java/com/king/app/dialog/BaseDialogConfig.java +++ b/app-dialog/src/main/java/com/king/app/dialog/BaseDialogConfig.java @@ -12,6 +12,7 @@ import androidx.annotation.StringRes; import androidx.annotation.StyleRes; /** + * 基础对话框配置 * @author Jenly */ 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; diff --git a/app-dialog/src/main/java/com/king/app/dialog/fragment/AppDialogFragment.java b/app-dialog/src/main/java/com/king/app/dialog/fragment/AppDialogFragment.java index 9b34ff6..3cfaafa 100644 --- a/app-dialog/src/main/java/com/king/app/dialog/fragment/AppDialogFragment.java +++ b/app-dialog/src/main/java/com/king/app/dialog/fragment/AppDialogFragment.java @@ -11,6 +11,7 @@ import com.king.app.dialog.BaseDialogConfig; import com.king.app.dialog.R; /** + * App对话框 Fragment:封装便捷的对话框API,使用时更简单 * @author Jenly Jenly */ 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; diff --git a/app-dialog/src/main/java/com/king/app/dialog/fragment/BaseDialogFragment.java b/app-dialog/src/main/java/com/king/app/dialog/fragment/BaseDialogFragment.java index da58745..ae1e3a2 100644 --- a/app-dialog/src/main/java/com/king/app/dialog/fragment/BaseDialogFragment.java +++ b/app-dialog/src/main/java/com/king/app/dialog/fragment/BaseDialogFragment.java @@ -20,6 +20,7 @@ import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; /** + * 基础对话框 Fragment * @author Jenly Jenly */ 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); } \ No newline at end of file diff --git a/app-updater/src/main/AndroidManifest.xml b/app-updater/src/main/AndroidManifest.xml index 16bef19..25e3e86 100644 --- a/app-updater/src/main/AndroidManifest.xml +++ b/app-updater/src/main/AndroidManifest.xml @@ -2,11 +2,8 @@ package="com.king.app.updater"> - - - @@ -15,7 +12,7 @@ Jenly */ 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 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 { + private static class DownloadTask extends AsyncTask { 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 requestProperty, DownloadCallback callback){ + private int timeout; + + private volatile boolean isCancel; + + public DownloadTask(String url, String path, String filename, int timeout, @Nullable Map 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 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 { } } - } } \ 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 7d71d63..47ba9ce 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,7 +8,7 @@ import java.util.Map; import androidx.annotation.Nullable; /** - * IHttpManager 默认提供{@link HttpManager} 和 {@link OkHttpManager}两种实现。 + * IHttpManager 默认提供 {@link HttpManager} 和 {@link OkHttpManager} 两种实现。 * @author Jenly Jenly */ public interface IHttpManager { 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 index a1b00a8..42d181f 100644 --- 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 @@ -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} + *

使用 OkHttpManager 时必须依赖 OkHttp 库 * @author Jenly */ 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 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 { + private static class DownloadTask extends AsyncTask { + 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 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())); } diff --git a/app-updater/src/main/java/com/king/app/updater/notify/INotification.java b/app-updater/src/main/java/com/king/app/updater/notify/INotification.java new file mode 100644 index 0000000..2f7c41f --- /dev/null +++ b/app-updater/src/main/java/com/king/app/updater/notify/INotification.java @@ -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 Jenly + */ +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); +} diff --git a/app-updater/src/main/java/com/king/app/updater/notify/NotificationImpl.java b/app-updater/src/main/java/com/king/app/updater/notify/NotificationImpl.java new file mode 100644 index 0000000..1ab4108 --- /dev/null +++ b/app-updater/src/main/java/com/king/app/updater/notify/NotificationImpl.java @@ -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 Jenly + */ +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); + } +} 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 2d7d05f..0507670 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,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 Jenly */ 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(); @@ -70,10 +67,10 @@ public class DownloadService extends Service { mCount++; } //获取配置信息 - UpdateConfig config = intent.getParcelableExtra(Constants.KEY_UPDATE_CONFIG); - startDownload(config,null,null); + UpdateConfig config = intent.getParcelableExtra(Constants.KEY_UPDATE_CONFIG); + 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 AppDownloadCallback(UpdateConfig config,UpdateCallback callback){ + private INotification notification; + + /** + * 最后更新进度,用来降频刷新 + */ + private int lastProgress; + /** + * 最后进度更新时间,用来降频刷新 + */ + private long lastTime; + /** + * APK文件 + */ + private File apkFile; + + + 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); + } } 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 9280677..893f8d0 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 @@ -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 diff --git a/app-updater/src/main/java/com/king/app/updater/util/LogUtils.java b/app-updater/src/main/java/com/king/app/updater/util/LogUtils.java new file mode 100644 index 0000000..3a1ad2e --- /dev/null +++ b/app-updater/src/main/java/com/king/app/updater/util/LogUtils.java @@ -0,0 +1,316 @@ +/* + Copyright © 2015, 2016 Jenly Yu Jenly + + 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 Jenly + */ +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); + } + +} diff --git a/app-updater/src/main/java/com/king/app/updater/util/NotificationUtils.java b/app-updater/src/main/java/com/king/app/updater/util/NotificationUtils.java index 89a5c53..e7a394e 100644 --- a/app-updater/src/main/java/com/king/app/updater/util/NotificationUtils.java +++ b/app-updater/src/main/java/com/king/app/updater/util/NotificationUtils.java @@ -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){//重新下载 - Intent intent = new Intent(context, DownloadService.class); + 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; + } } 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 cd45154..2f4fd29 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 @@ -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); - return false; + 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"; 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 840d084..f4927b6 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 @@ -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) { @@ -46,7 +49,7 @@ public final class SSLSocketFactoryUtils { public static X509TrustManager createTrustAllManager() { X509TrustManager tm = null; try { - tm = new X509TrustManager() { + tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { //do nothing @@ -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) { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, getTrustManager(certificates), new SecureRandom()); + sSLSocketFactory = sslContext.getSocketFactory(); + } catch (Exception e) { e.printStackTrace(); - return null; } - //获得服务器端证书 - TrustManager[] turstManager = getTrustManager(certificates); - - //初始化ssl证书库 - try { - sslContext.init(null,turstManager,new SecureRandom()); - } catch (KeyManagementException 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()}; } diff --git a/app/release/app-release.apk b/app/release/app-release.apk index b116e86..741f1a5 100644 Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 777296f..a0992d1 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -10,8 +10,8 @@ { "type": "SINGLE", "filters": [], - "versionCode": 21, - "versionName": "1.1.2", + "versionCode": 22, + "versionName": "1.1.3", "outputFile": "app-release.apk" } ] diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8e0b0f8..42ae627 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,9 @@ + + +