Compare commits

..

24 Commits

Author SHA1 Message Date
Jenly ad203b292d Update README 4 years ago
Jenly 00fd88c259 maven publish 4 years ago
Jenly ed24593d5c 优化细节 4 years ago
Jenly b77f795b3d 优化细节 4 years ago
Jenly 2263780073 优化细节 4 years ago
Jenly cbb98f0ff5 优化细节 4 years ago
Jenly 165709991f * AppDialogConfig添加构造参数,简化自定义扩展用法 4 years ago
Jenly 6d6848ed94 优化细节 4 years ago
Jenly 0f335e154b 优化细节 4 years ago
Jenly 23a99f6cd9 * 优化默认Dialog样式的显示细节 4 years ago
Jenly dc5ff74a9e 优化细节 4 years ago
jenly1314 21d9d4215e 支持MD5校验 5 years ago
jenly1314 46c743cb26 支持MD5校验 5 years ago
jenly1314 888ce12ddc 支持重定向 (#7) 5 years ago
jenly1314 32e71c030e 优化细节 5 years ago
jenly1314 946b6e8550 增加混淆 5 years ago
jenly1314 d7a9bd8dd0 优化细节 5 years ago
jenly1314 6dee0be157 优化细节 5 years ago
jenly1314 b68626587a CI 5 years ago
jenly1314 bd74ae12ce 支持取消下载 5 years ago
jenly1314 62210f4b88 支持取消下载 5 years ago
jenly1314 76f489f8d2 CI 5 years ago
jenly1314 4f4e977604 CI 5 years ago
jenly1314 38d7641303 支持AndroidX版本 5 years ago
  1. 20
      .github/workflows/gradle.yml
  2. 6
      .travis.yml
  3. 96
      README.md
  4. 13
      app-dialog/build.gradle
  5. 6
      app-dialog/src/androidTest/java/com/king/app/dialog/ExampleInstrumentedTest.java
  6. 333
      app-dialog/src/main/java/com/king/app/dialog/AppDialog.java
  7. 682
      app-dialog/src/main/java/com/king/app/dialog/AppDialogConfig.java
  8. 470
      app-dialog/src/main/java/com/king/app/dialog/BaseDialogConfig.java
  9. 22
      app-dialog/src/main/java/com/king/app/dialog/fragment/AppDialogFragment.java
  10. 81
      app-dialog/src/main/java/com/king/app/dialog/fragment/BaseDialogFragment.java
  11. 9
      app-dialog/src/main/res/anim/app_dialog_bottom_in.xml
  12. 9
      app-dialog/src/main/res/anim/app_dialog_bottom_out.xml
  13. 5
      app-dialog/src/main/res/anim/app_dialog_in.xml
  14. 9
      app-dialog/src/main/res/anim/app_dialog_left_in.xml
  15. 9
      app-dialog/src/main/res/anim/app_dialog_left_out.xml
  16. 5
      app-dialog/src/main/res/anim/app_dialog_out.xml
  17. 9
      app-dialog/src/main/res/anim/app_dialog_right_in.xml
  18. 9
      app-dialog/src/main/res/anim/app_dialog_right_out.xml
  19. 16
      app-dialog/src/main/res/anim/app_dialog_scale_bottom_in.xml
  20. 16
      app-dialog/src/main/res/anim/app_dialog_scale_bottom_out.xml
  21. 16
      app-dialog/src/main/res/anim/app_dialog_scale_left_in.xml
  22. 16
      app-dialog/src/main/res/anim/app_dialog_scale_left_out.xml
  23. 16
      app-dialog/src/main/res/anim/app_dialog_scale_right_in.xml
  24. 16
      app-dialog/src/main/res/anim/app_dialog_scale_right_out.xml
  25. 16
      app-dialog/src/main/res/anim/app_dialog_scale_top_in.xml
  26. 16
      app-dialog/src/main/res/anim/app_dialog_scale_top_out.xml
  27. 9
      app-dialog/src/main/res/anim/app_dialog_top_in.xml
  28. 9
      app-dialog/src/main/res/anim/app_dialog_top_out.xml
  29. 16
      app-dialog/src/main/res/layout/app_dialog.xml
  30. 4
      app-dialog/src/main/res/values/colors.xml
  31. 2
      app-dialog/src/main/res/values/strings.xml
  32. 51
      app-dialog/src/main/res/values/styles.xml
  33. 15
      app-updater/build.gradle
  34. 5
      app-updater/src/androidTest/java/com/king/app/updater/ExampleInstrumentedTest.java
  35. 5
      app-updater/src/main/AndroidManifest.xml
  36. 39
      app-updater/src/main/java/com/king/app/updater/AppUpdater.java
  37. 119
      app-updater/src/main/java/com/king/app/updater/UpdateConfig.java
  38. 2
      app-updater/src/main/java/com/king/app/updater/callback/UpdateCallback.java
  39. 2
      app-updater/src/main/java/com/king/app/updater/constant/Constants.java
  40. 149
      app-updater/src/main/java/com/king/app/updater/http/HttpManager.java
  41. 5
      app-updater/src/main/java/com/king/app/updater/http/IHttpManager.java
  42. 32
      app-updater/src/main/java/com/king/app/updater/http/OkHttpManager.java
  43. 25
      app-updater/src/main/java/com/king/app/updater/notify/INotification.java
  44. 38
      app-updater/src/main/java/com/king/app/updater/notify/NotificationImpl.java
  45. 3
      app-updater/src/main/java/com/king/app/updater/provider/AppUpdaterFileProvider.java
  46. 269
      app-updater/src/main/java/com/king/app/updater/service/DownloadService.java
  47. 41
      app-updater/src/main/java/com/king/app/updater/util/AppUtils.java
  48. 316
      app-updater/src/main/java/com/king/app/updater/util/LogUtils.java
  49. 68
      app-updater/src/main/java/com/king/app/updater/util/NotificationUtils.java
  50. 41
      app-updater/src/main/java/com/king/app/updater/util/PermissionUtils.java
  51. 67
      app-updater/src/main/java/com/king/app/updater/util/SSLSocketFactoryUtils.java
  52. 1
      app-updater/src/main/res/xml/app_updater_paths.xml
  53. 17
      app/build.gradle
  54. BIN
      app/release/app-release.apk
  55. 18
      app/release/output-metadata.json
  56. 1
      app/release/output.json
  57. 5
      app/src/androidTest/java/com/king/appupdater/ExampleInstrumentedTest.java
  58. 3
      app/src/main/AndroidManifest.xml
  59. 108
      app/src/main/java/com/king/appupdater/MainActivity.java
  60. 5
      app/src/main/res/color/btn_cancel_color_selector.xml
  61. 18
      app/src/main/res/drawable/btn_confirm_selector.xml
  62. 17
      app/src/main/res/layout/activity_main.xml
  63. 6
      app/src/main/res/layout/dialog.xml
  64. 43
      app/src/main/res/layout/dialog_custom.xml
  65. 25
      app/src/main/res/layout/dialog_heads_up.xml
  66. 43
      app/src/main/res/layout/dialog_progress.xml
  67. 11
      app/src/main/res/values/colors.xml
  68. 2
      app/src/main/res/values/strings.xml
  69. 11
      app/src/main/res/values/styles.xml
  70. 25
      build.gradle
  71. 9
      gradle.properties
  72. 54
      versions.gradle

@ -1,20 +0,0 @@
name: Android CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: ./gradlew build

@ -1,12 +1,12 @@
language: android
jdk: oraclejdk8
before_install:
- yes | sdkmanager "platforms;android-29"
- yes | sdkmanager "platforms;android-28"
env:
global:
- ANDROID_API_LEVEL=29
- ANDROID_BUILD_TOOLS_VERSION=29.0.3
- ANDROID_API_LEVEL=28
- ANDROID_BUILD_TOOLS_VERSION=28.0.3
- TRAVIS_SECURE_ENV_VARS=true
android:

@ -3,7 +3,6 @@
![Image](app/src/main/ic_launcher-web.png)
[![Download](https://img.shields.io/badge/download-App-blue.svg)](https://raw.githubusercontent.com/jenly1314/AppUpdater/master/app/release/app-release.apk)
[![MavenCentral](https://img.shields.io/maven-central/v/com.github.jenly1314.AppUpdater/app-updater)](https://repo1.maven.org/maven2/com/github/jenly1314/AppUpdater)
[![JCenter](https://img.shields.io/badge/JCenter-1.0.10-46C018.svg)](https://bintray.com/beta/#/jenly/maven/app-updater)
[![JitPack](https://jitpack.io/v/jenly1314/AppUpdater.svg)](https://jitpack.io/#jenly1314/AppUpdater)
[![CI](https://travis-ci.org/jenly1314/AppUpdater.svg?branch=master)](https://travis-ci.org/jenly1314/AppUpdater)
@ -28,47 +27,34 @@ 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)
> 你也可以直接下载 [演示App](https://raw.githubusercontent.com/jenly1314/AppUpdater/master/app/release/app-release.apk) 体验效果
## 引入
### Gradle:
1. 在Project的 **build.gradle** 里面添加远程仓库
```gradle
allprojects {
repositories {
//...
mavenCentral()
}
}
```
2. 在Module的 **build.gradle** 里面添加引入依赖项
```gradle
//----------AndroidX 版本
### Maven:
```maven
//app-updater
implementation 'com.github.jenly1314.AppUpdater:app-updater:1.1.3'
//app-dialog
implementation 'com.github.jenly1314.AppUpdater:app-dialog:1.1.3'
<dependency>
<groupId>com.king.app</groupId>
<artifactId>app-updater</artifactId>
<version>1.0.10</version>
<type>pom</type>
</dependency>
//app-dialog
<dependency>
<groupId>com.king.app</groupId>
<artifactId>app-dialog</artifactId>
<version>1.0.10</version>
<type>pom</type>
</dependency>
```
以前发布至JCenter的版本
### Gradle:
```gradle
//----------AndroidX 版本
@ -83,6 +69,28 @@ allprojects {
//app-dialog
implementation 'com.king.app:app-dialog:1.0.10'
```
### Lvy:
```lvy
//app-updater
<dependency org='com.king.app' name='app-dialog' rev='1.0.10'>
<artifact name='$AID' ext='pom'></artifact>
</dependency>
//app-dialog
<dependency org='com.king.app' name='app-dialog' rev='1.0.10'>
<artifact name='$AID' ext='pom'></artifact>
</dependency>
```
###### 如果Gradle出现compile失败的情况,可以在Project的build.gradle里面添加如下:(也可以使用上面的GitPack来complie)
```gradle
allprojects {
repositories {
//...
maven { url 'https://dl.bintray.com/jenly/maven' }
}
}
```
## 示例
@ -94,9 +102,9 @@ allprojects {
//简单弹框升级
AppDialogConfig config = new AppDialogConfig(context);
config.setTitle("简单弹框升级")
.setConfirm("升级") //旧版本使用setOk
.setOk("升级")
.setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
.setOnClickConfirm(new View.OnClickListener() { //旧版本使用setOnClickOk
.setOnClickOk(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AppUpdater.Builder()
@ -112,9 +120,9 @@ allprojects {
//简单DialogFragment升级
AppDialogConfig config = new AppDialogConfig(context);
config.setTitle("简单DialogFragment升级")
.setConfirm("升级") //旧版本使用setOk
.setOk("升级")
.setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
.setOnClickConfirm(new View.OnClickListener() { //旧版本使用setOnClickOk
.setOnClickOk(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AppUpdater.Builder()
@ -130,7 +138,7 @@ allprojects {
```
更多使用详情,请查看[app](app)中的源码使用示例或直接查看[API帮助文档](https://javadoc.jitpack.io/com/github/jenly1314/AppUpdater/1.1.0/javadoc/index.html)
更多使用详情,请查看[app](app)中的源码使用示例或直接查看[API帮助文档](https://jenly1314.github.io/projects/AppUpdater/doc/)
## 混淆
@ -140,22 +148,6 @@ allprojects {
## 版本记录
#### v1.1.3:2022-04-25
* 统一日志管理
* 适配Android 12(S)
* 优化细节
#### v1.1.2:2021-11-18
* AppDialog对外提供更多与WindowManager.LayoutParams相关的配置
#### v1.1.1:2021-9-14
* 对外提供更多可配置参数
* 优化细节
#### v1.1.0:2021-7-2 (从v1.1.0开始不再发布至JCenter)
* 后续版本只支持androidx,版本名称不再带有androidx标识
* 优化细节
#### v1.0.10:2021-3-4
* AppDialogConfig添加构造参数,简化自定义扩展用法
* 优化细节

@ -12,7 +12,7 @@ android {
targetSdkVersion build_versions.targetSdk
versionCode app_version.versionCode
versionName app_version.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
@ -32,11 +32,11 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation "junit:junit:$versions.junit"
androidTestImplementation "androidx.test.ext:junit:$versions.androidExtJunit"
androidTestImplementation "androidx.test.espresso:espresso-core:$versions.espressoCore"
compileOnly "androidx.appcompat:appcompat:$versions.appcompat"
testImplementation deps.test.junit
androidTestImplementation deps.test.runner
androidTestImplementation deps.test.espresso
//support
compileOnly deps.support.appcompat
}
@ -46,3 +46,4 @@ dependencies {

@ -1,10 +1,11 @@
package com.king.app.dialog;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import androidx.test.InstrumentationRegistry;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
@ -13,6 +14,7 @@ import static org.junit.Assert.*;
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {

@ -3,7 +3,10 @@ package com.king.app.dialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.Gravity;
import android.support.annotation.NonNull;
import android.support.annotation.StyleRes;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
@ -11,24 +14,32 @@ import android.view.WindowManager;
import com.king.app.dialog.fragment.AppDialogFragment;
import androidx.annotation.StyleRes;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
/**
* App对话框封装便捷的对话框API使用时更简单
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public enum AppDialog {
INSTANCE;
final float DEFAULT_WIDTH_RATIO = 0.85f;
private final float DEFAULT_WIDTH_RATIO = 0.85f;
private Dialog mDialog;
private String mTag;
//-------------------------------------------
/**
* @param context
* @param config 弹框配置 {@link AppDialogConfig}
* @return
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
@Deprecated
public View createAppDialogView(@NonNull Context context,@NonNull AppDialogConfig config){
return config.buildAppDialogView();
}
//-------------------------------------------
@ -41,20 +52,11 @@ 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);
@ -62,12 +64,8 @@ public enum AppDialog {
}
}
/**
* {@link DialogFragment#dismiss()}
* @param dialogFragment {@link DialogFragment}
*/
public void dismissDialogFragment(DialogFragment dialogFragment){
if(dialogFragment != null){
if(dialogFragment!=null){
dialogFragment.dismiss();
}
}
@ -76,8 +74,7 @@ public enum AppDialog {
/**
* 显示DialogFragment
* @param fragmentManager {@link FragmentManager}
* @param config {@link AppDialogConfig}
* @param fragmentManager
* @return
*/
public String showDialogFragment(FragmentManager fragmentManager,AppDialogConfig config){
@ -90,8 +87,8 @@ public enum AppDialog {
/**
* 显示DialogFragment
* @param fragmentManager {@link FragmentManager}
* @param dialogFragment {@link DialogFragment}
* @param fragmentManager
* @param dialogFragment
* @return
*/
public String showDialogFragment(FragmentManager fragmentManager,DialogFragment dialogFragment){
@ -103,9 +100,9 @@ public enum AppDialog {
/**
* 显示DialogFragment
* @param fragmentManager {@link FragmentManager}
* @param dialogFragment {@link DialogFragment}
* @param tag dialogFragment对应的标签
* @param fragmentManager
* @param dialogFragment
* @param tag
* @return
*/
public String showDialogFragment(FragmentManager fragmentManager,DialogFragment dialogFragment, String tag) {
@ -126,38 +123,41 @@ public enum AppDialog {
}
/**
* 显示弹框
* 显示弹框 请使用{@link #showDialog(AppDialogConfig)}
* @param context
* @param config 弹框配置 {@link AppDialogConfig}
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
public void showDialog(AppDialogConfig config,boolean isCancel){
showDialog(config.getContext(),config, isCancel);
@Deprecated
public void showDialog(Context context,AppDialogConfig config){
showDialog(config,true);
}
/**
* 显示弹框
* @param context 上下文
* 显示弹框请使用{@link #showDialog(AppDialogConfig, boolean)}
* @param context
* @param config 弹框配置 {@link AppDialogConfig}
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
public void showDialog(Context context,AppDialogConfig config){
showDialog(context,config,true);
@Deprecated
public void showDialog(Context context,AppDialogConfig config,boolean isCancel){
showDialog(config,isCancel);
}
/**
* 显示弹框
* @param context 上下文
* @param config 弹框配置 {@link AppDialogConfig}
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
public void showDialog(Context context,AppDialogConfig config,boolean isCancel){
showDialog(context, config.buildAppDialogView(), config.getStyleId(), config.getGravity(),
config.getWidthRatio(), config.x, config.y, config.horizontalMargin, config.verticalMargin,
config.horizontalWeight, config.verticalWeight, isCancel);
public void showDialog(AppDialogConfig config,boolean isCancel){
showDialog(config.getContext(),config.buildAppDialogView(),config.getStyleId(),DEFAULT_WIDTH_RATIO,isCancel);
}
/**
* 显示弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
*/
public void showDialog(Context context,View contentView){
@ -166,7 +166,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
@ -176,7 +176,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param widthRatio 宽度比例根据屏幕宽度计算得来
*/
@ -186,7 +186,7 @@ public enum AppDialog {
/**
* 显示弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param isCancel 是否可取消默认为truefalse则拦截back键
@ -195,10 +195,9 @@ public enum AppDialog {
showDialog(context,contentView,R.style.app_dialog,widthRatio,isCancel);
}
/**
* 显示弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
@ -209,121 +208,28 @@ public enum AppDialog {
/**
* 显示弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
*/
public void showDialog(Context context, View contentView, @StyleRes int styleId, int gravity, float widthRatio){
showDialog(context,contentView,styleId,gravity, widthRatio, true);
}
/**
* 显示弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
public void showDialog(Context context, View contentView, @StyleRes int styleId, float widthRatio,final boolean isCancel){
showDialog(context,contentView,styleId,Gravity.NO_GRAVITY,widthRatio,isCancel);
}
/**
* 显示弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
public void showDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio,final boolean isCancel){
showDialog(context, contentView, styleId, gravity, widthRatio, 0, 0, isCancel);
}
/**
* 显示弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @return
*/
public void showDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio, int x, int y, final boolean isCancel){
showDialog(context, contentView, styleId, gravity, widthRatio, x, y, 0, 0, isCancel);
}
/**
* 显示弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param horizontalMargin 水平方向边距
* @param verticalMargin 垂直方向边距
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @return
*/
public void showDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio, int x, int y, float horizontalMargin, float verticalMargin, final boolean isCancel){
showDialog(context, contentView, styleId, gravity, widthRatio, x, y, horizontalMargin, verticalMargin, 0, 0, isCancel);
}
/**
* 显示弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param horizontalMargin 水平方向边距
* @param verticalMargin 垂直方向边距
* @param horizontalWeight 水平方向权重
* @param verticalWeight 垂直方向权重
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @return
*/
public void showDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio, int x, int y, float horizontalMargin, float verticalMargin, float horizontalWeight, float verticalWeight, final boolean isCancel){
dismissDialog();
mDialog = createDialog(context, contentView, styleId, gravity, widthRatio, x, y, horizontalMargin, verticalMargin, horizontalWeight, verticalWeight, isCancel);
mDialog = createDialog(context,contentView,styleId,widthRatio,isCancel);
mDialog.show();
}
/**
* 设置弹框窗口配置
* @param context 上下文
* @param dialog Dialog对话框
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param horizontalMargin 水平方向边距
* @param verticalMargin 垂直方向边距
* @param horizontalWeight 水平方向权重
* @param verticalWeight 垂直方向权重
* @param context
* @param dialog
* @param widthRatio
*/
private void setDialogWindow(Context context,Dialog dialog,int gravity,float widthRatio, int x, int y, float horizontalMargin, float verticalMargin, float horizontalWeight, float verticalWeight){
private void setDialogWindow(Context context,Dialog dialog,float widthRatio){
Window window = dialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = (int)(context.getResources().getDisplayMetrics().widthPixels * widthRatio);
lp.gravity = gravity;
lp.x = x;
lp.y = y;
lp.horizontalMargin = horizontalMargin;
lp.verticalMargin = verticalMargin;
lp.horizontalWeight = horizontalWeight;
lp.verticalWeight = verticalWeight;
window.setAttributes(lp);
}
@ -336,36 +242,40 @@ public enum AppDialog {
}
/**
* 创建弹框
* 创建弹框请使用{@link #createDialog(AppDialogConfig)}
* @param context
* @param config 弹框配置 {@link AppDialogConfig}
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
public Dialog createDialog(AppDialogConfig config,boolean isCancel){
return createDialog(config.getContext(),config.buildAppDialogView(),config.getStyleId(),DEFAULT_WIDTH_RATIO,isCancel);
@Deprecated
public Dialog createDialog(Context context,AppDialogConfig config){
return createDialog(config,true);
}
/**
* 创建弹框
* @param context 上下文
* 创建弹框请使用{@link #createDialog(AppDialogConfig, boolean)}
* @param context
* @param config 弹框配置 {@link AppDialogConfig}
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
public Dialog createDialog(Context context,AppDialogConfig config){
return createDialog(context,config,true);
@Deprecated
public Dialog createDialog(Context context,AppDialogConfig config,boolean isCancel){
return createDialog(config,isCancel);
}
/**
* 创建弹框
* @param context 上下文
* @param config 弹框配置 {@link AppDialogConfig}
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
public Dialog createDialog(Context context,AppDialogConfig config,boolean isCancel){
return createDialog(context,config.buildAppDialogView(),config.getStyleId(),DEFAULT_WIDTH_RATIO,isCancel);
public Dialog createDialog(AppDialogConfig config,boolean isCancel){
return createDialog(config.getContext(),config.buildAppDialogView(),config.getStyleId(),DEFAULT_WIDTH_RATIO,isCancel);
}
/**
* 创建弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
*/
public Dialog createDialog(Context context,View contentView){
@ -374,7 +284,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
@ -384,7 +294,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param widthRatio 宽度比例根据屏幕宽度计算得来
*/
@ -394,7 +304,7 @@ public enum AppDialog {
/**
* 创建弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param isCancel 是否可取消默认为truefalse则拦截back键
@ -405,104 +315,24 @@ public enum AppDialog {
/**
* 创建弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
*/
public Dialog createDialog(Context context, View contentView, @StyleRes int styleId, float widthRatio){
return createDialog(context,contentView,styleId,Gravity.NO_GRAVITY,widthRatio,true);
return createDialog(context,contentView,styleId,widthRatio,true);
}
/**
* 创建弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
*/
public Dialog createDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio){
return createDialog(context,contentView,styleId,gravity,widthRatio,true);
}
/**
* 创建弹框
* @param context 上下文
* @param context
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
public Dialog createDialog(Context context, View contentView, @StyleRes int styleId, float widthRatio,final boolean isCancel){
return createDialog(context,contentView,styleId,Gravity.NO_GRAVITY,widthRatio,isCancel);
}
/**
* 创建弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param isCancel 是否可取消默认为truefalse则拦截back键
*/
public Dialog createDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio,final boolean isCancel){
return createDialog(context, contentView, styleId, gravity, widthRatio, 0, 0, isCancel);
}
/**
* 创建弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @return
*/
public Dialog createDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio, int x, int y,final boolean isCancel){
return createDialog(context, contentView, styleId, gravity, widthRatio, x, y, 0, 0, isCancel);
}
/**
* 创建弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param horizontalMargin 水平方向边距
* @param verticalMargin 垂直方向边距
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @return
*/
public Dialog createDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio, int x, int y, float horizontalMargin, float verticalMargin,final boolean isCancel){
return createDialog(context, contentView, styleId, gravity, widthRatio, x, y, horizontalMargin, verticalMargin, 0, 0, isCancel);
}
/**
* 创建弹框
* @param context 上下文
* @param contentView 弹框内容视图
* @param styleId Dialog样式
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param horizontalMargin 水平方向边距
* @param verticalMargin 垂直方向边距
* @param horizontalWeight 水平方向权重
* @param verticalWeight 垂直方向权重
* @param isCancel 是否可取消默认为truefalse则拦截back键
* @return
*/
public Dialog createDialog(Context context, View contentView, @StyleRes int styleId,int gravity, float widthRatio, int x, int y, float horizontalMargin, float verticalMargin, float horizontalWeight, float verticalWeight,final boolean isCancel){
Dialog dialog = new Dialog(context,styleId);
dialog.setContentView(contentView);
dialog.setCanceledOnTouchOutside(false);
@ -519,30 +349,19 @@ public enum AppDialog {
}
});
setDialogWindow(context, dialog, gravity, widthRatio, x, y, horizontalMargin, verticalMargin, horizontalWeight, verticalWeight);
setDialogWindow(context,dialog,widthRatio);
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();

@ -1,36 +1,17 @@
package com.king.app.dialog;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.util.Linkify;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RatingBar;
import android.widget.TextView;
import java.util.regex.Pattern;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
/**
* App 对话框配置
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class AppDialogConfig extends BaseDialogConfig{
@ -40,39 +21,25 @@ public class AppDialogConfig extends BaseDialogConfig{
private View view;
private ViewHolder viewHolder;
/**
* 构造
* @param context 上下文
*/
public AppDialogConfig(@NonNull Context context){
this(context, R.layout.app_dialog);
this(context,R.layout.app_dialog);
}
/**
* 构造
* @param context 上下文
* @param layoutId 布局ID
*/
public AppDialogConfig(@NonNull Context context, @LayoutRes int layoutId){
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 对话框视图
* use {@link #getDialogView()}
* @param context
* @return
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
@Deprecated
@ -80,33 +47,17 @@ public class AppDialogConfig extends BaseDialogConfig{
return getDialogView();
}
/**
* 获取对话框视图
* @return 对话框视图
*/
private View getDialogView(){
public View getDialogView(){
if(view == null){
view = LayoutInflater.from(context).inflate(getLayoutId(),null);
}
return view;
}
/**
* 通过视图ID查找对应的视图
* @param id 视图ID
* @param <T> 对应的视图类
* @return 视图ID对应的视图
*/
private <T extends View> T findView(@IdRes int id){
return getDialogView().findViewById(id);
return (T)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){
@ -117,9 +68,10 @@ public class AppDialogConfig extends BaseDialogConfig{
return (T)v;
}
/**
* 通过{@link AppDialogConfig} 创建一个视图
* @return {@link View}
* @return
*/
View buildAppDialogView(){
TextView tvDialogTitle = getView(titleId);
@ -145,617 +97,21 @@ public class AppDialogConfig extends BaseDialogConfig{
line.setVisibility(isHideCancel ? View.GONE : View.VISIBLE);
}
Button btnDialogConfirm = getView(confirmId);
if(btnDialogConfirm != null){
setText(btnDialogConfirm,confirm);
btnDialogConfirm.setOnClickListener(onClickConfirm != null ? onClickConfirm : AppDialog.INSTANCE.mOnClickDismissDialog);
Button btnDialogOK = getView(okId);
if(btnDialogOK != null){
setText(btnDialogOK,ok);
btnDialogOK.setOnClickListener(onClickOk != null ? onClickOk : AppDialog.INSTANCE.mOnClickDismissDialog);
}
return view;
}
private void setText(TextView tv, CharSequence text){
private void setText(TextView tv,CharSequence text){
if(text != null){
tv.setText(text);
}
}
/**
* 获取 {@link ViewHolder}
* @return
*/
public final ViewHolder getViewHolder(){
if(viewHolder == null){
viewHolder = new ViewHolder();
}
return viewHolder;
}
/**
* ViewHolder主要提供控件的一些常用设置适用于Dialog不适用于DialogFragment
*/
public final class ViewHolder {
private ViewHolder(){
}
//---------------------- 控件常用设置
/**
* 设置视图的背景色
* {@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);
v.setBackground(drawable);
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;
}
/**
* 设置视图的可见性
* {@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){
v.setVisibility(View.VISIBLE);
}else{
v.setVisibility(View.GONE);
}
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, 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);
ratingBar.setMax(max);
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();
}
//---------------------- 监听事件
/**
* 设置点击监听事
* {@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);
}
}
}

@ -1,19 +1,15 @@
package com.king.app.dialog;
import android.content.Context;
import android.view.Gravity;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.annotation.StyleRes;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
/**
* 基础对话框配置
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class BaseDialogConfig {
@ -36,7 +32,7 @@ public class BaseDialogConfig {
/**
* 确定视图ID右边按钮
*/
@IdRes int confirmId = R.id.btnDialogConfirm;
@IdRes int okId = R.id.btnDialogOK;
/**
* 按钮中间分割线ID
*/
@ -47,11 +43,6 @@ public class BaseDialogConfig {
*/
@StyleRes int styleId = R.style.app_dialog;
/**
* 对话框动画样式ID
*/
@StyleRes int animationStyleId = R.style.app_dialog_scale_animation;
/**
* 标题文本
*/
@ -67,7 +58,7 @@ public class BaseDialogConfig {
/**
* 确定按钮文本
*/
CharSequence confirm;
CharSequence ok;
/**
* 是否隐藏取消按钮如果隐藏取消则底部只显示一个按钮
*/
@ -77,593 +68,184 @@ public class BaseDialogConfig {
*/
boolean isHideTitle;
/**
* 宽度比例根据屏幕宽度计算得来
*/
float widthRatio = AppDialog.INSTANCE.DEFAULT_WIDTH_RATIO;
/**
* Dialog对齐方式 {@link WindowManager.LayoutParams#gravity}
*
*/
int gravity = Gravity.NO_GRAVITY;
/**
* {@link WindowManager.LayoutParams#x}
*/
int x;
/**
* {@link WindowManager.LayoutParams#y}
*/
int y;
/**
* {@link WindowManager.LayoutParams#verticalMargin}
*/
float verticalMargin;
/**
* {@link WindowManager.LayoutParams#horizontalMargin}
*/
float horizontalMargin;
/**
* {@link WindowManager.LayoutParams#horizontalWeight}
*/
float horizontalWeight;
/**
* {@link WindowManager.LayoutParams#verticalWeight}
*/
float verticalWeight;
View.OnClickListener onClickCancel;
View.OnClickListener onClickConfirm;
View.OnClickListener onClickOk;
/**
* 构造
*/
public BaseDialogConfig(){
this(R.layout.app_dialog);
}
/**
* 构造
* @param layoutId 布局ID
*/
public BaseDialogConfig(@LayoutRes int layoutId){
this.layoutId = layoutId;
}
/**
* 布局ID
* @return 布局ID
*/
public @LayoutRes int getLayoutId() {
return layoutId;
}
/**
* 此方法即将废弃请通过构造{@link #BaseDialogConfig(int)}来初始化
* @param layoutId 布局ID
* @return {@link BaseDialogConfig}
* @param layoutId
* @return
* @deprecated 即将废弃下一个版本可能会移除此方法
*/
@Deprecated
public BaseDialogConfig setLayoutId(@LayoutRes int layoutId) {
public BaseDialogConfig setLayoutId(@IdRes int layoutId) {
this.layoutId = layoutId;
return this;
}
/**
* 标题视图ID
* @return 视图ID
*/
public int getTitleId() {
return titleId;
}
/**
* 设置标题视图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;
}
/**
* 设置Dialog样式ID(仅对Dialog有效如果使用的是DialogFragment请使用{@link #setAnimationStyleId(int)})
* @param styleId
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setStyleId(@StyleRes int styleId) {
public BaseDialogConfig setStyleId(@IdRes int styleId) {
this.styleId = styleId;
return this;
}
/**
* 对话框动画样式ID
* @return
*/
public int getAnimationStyleId(){
return animationStyleId;
}
/**
* 对话框动画样式ID (仅对DialogFragment有效如果使用的是Dialog请使用{@link #setStyleId(int)})
* @param animationStyleId
* @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 内容视图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 取消按钮视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancelId(@IdRes int cancelId) {
this.cancelId = cancelId;
return this;
}
/**
* 获取确定按钮视图ID
* @return 确定按钮视图ID
* @Deprecated 请使用 {@link #getConfirmId()}来代替后续版本可能会移除此方法
*/
@Deprecated
public @IdRes int getOkId() {
return getConfirmId();
return okId;
}
/**
* 设置确定按钮视图ID
* @param okId 确定按钮视图ID
* @return {@link BaseDialogConfig}
* @Deprecated 请使用 {@link #setConfirmId(int)}来代替后续版本可能移除此方法
*/
@Deprecated
public BaseDialogConfig setOkId(@IdRes int okId) {
return setConfirmId(okId);
}
/**
* 获取确定按钮视图ID
* @return 视图ID
*/
public @IdRes int getConfirmId() {
return confirmId;
}
/**
* 设置确定按钮视图ID
* @param confirmId 确定按钮视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setConfirmId(@IdRes int confirmId) {
this.confirmId = confirmId;
this.okId = okId;
return this;
}
/**
* 分割线视图ID
* @return 视图ID
*/
public @IdRes int getLineId() {
return lineId;
}
/**
* 设置分割线视图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 {@link BaseDialogConfig}
*/
public BaseDialogConfig setTitle(CharSequence title) {
this.title = title;
return this;
}
/**
* 设置标题
* @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 {@link BaseDialogConfig}
*/
public BaseDialogConfig setContent(CharSequence content) {
this.content = content;
return this;
}
/**
* 取消按钮文本内容
* @return 取消按钮文本内容
*/
public CharSequence getCancel() {
return cancel;
}
/**
* 设置取消按钮文本内容
* @param cancel 取消按钮文本内容
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancel(CharSequence cancel) {
this.cancel = cancel;
return this;
}
/**
* 设置取消按钮文本内容
* @param context 上下文
* @param resId 取消按钮文本内容资源ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancel(@NonNull Context context, @StringRes int resId) {
this.cancel = context.getString(resId);
return this;
}
/**
* 获取确定按钮文本内容
* @return 确定按钮文本内容
* @deprecated 请使用 {@link #getConfirm()} 来代替后续版本可能会移除此方法
*/
public CharSequence getOk() {
return getConfirm();
return ok;
}
/**
* 设置确定按钮文本内容
* @param ok 确定按钮文本内容
* @return {@link BaseDialogConfig}
* @deprecated 请使用 {@link #setConfirm(CharSequence)} 来代替后续版本可能会移除此方法
*/
public BaseDialogConfig setOk(CharSequence ok) {
return setConfirm(ok);
}
/**
* 设置确定按钮文本内容
* @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 {@link BaseDialogConfig}
*/
public BaseDialogConfig setConfirm(CharSequence confirm) {
this.confirm = confirm;
this.ok = ok;
return this;
}
/**
* 设置确定按钮文本内容
* @param context 上下文
* @param resId 确定按钮文本内容资源ID
* @return {@link BaseDialogConfig}
*
*/
public BaseDialogConfig setConfirm(@NonNull Context context, @StringRes int resId) {
this.confirm = context.getString(resId);
public BaseDialogConfig setOk(@NonNull Context context, @StringRes int resId) {
this.ok = context.getString(resId);
return this;
}
/**
* 是否隐藏取消按钮
* @return {@link #isHideCancel}
*/
public boolean isHideCancel() {
return isHideCancel;
}
/**
* 设置是否隐藏取消按钮
* @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 {@link BaseDialogConfig}
*/
public BaseDialogConfig setHideTitle(boolean hideTitle){
isHideTitle = hideTitle;
return this;
}
/**
* Dialog的宽度比例根据屏幕宽度计算得来
* @return {@link #widthRatio}
*/
public float getWidthRatio() {
return widthRatio;
}
/**
* 设置Dialog的宽度比例根据屏幕宽度计算得来
* @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 Dialog的对齐方式
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setGravity(int gravity){
this.gravity = gravity;
return this;
}
/**
* 取消按钮点击监听不设置默认点击关闭弹框
* @return 取消按钮点击监听
*/
public View.OnClickListener getOnClickCancel() {
return onClickCancel;
}
/**
* 设置取消按钮点击监听不设置默认点击关闭弹框
* @param onClickCancel 取消按钮点击监听
* @return {@link BaseDialogConfig}
* @param onClickCancel
* @return
*/
public BaseDialogConfig setOnClickCancel(View.OnClickListener onClickCancel) {
this.onClickCancel = onClickCancel;
return this;
}
/**
* 确定按钮点击监听不设置默认点击关闭弹框
* @return 确定按钮点击监听
*/
public View.OnClickListener getOnClickConfirm() {
return onClickConfirm;
}
/**
* 设置确定按钮点击监听不设置默认点击关闭弹框
* @param onClickConfirm 确定按钮点击监听
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setOnClickConfirm(View.OnClickListener onClickConfirm) {
this.onClickConfirm = onClickConfirm;
return this;
}
/**
* 获取确定按钮点击监听不设置默认点击关闭弹框
* @return 确定按钮点击监听
* @deprecated 请使用 {@link #getOnClickConfirm()}来代替后续版本可能会移除此方法
*/
public View.OnClickListener getOnClickOk() {
return getOnClickConfirm();
return onClickOk;
}
/**
* 设置确定按钮点击监听不设置默认点击关闭弹框
* @param onClickOk 确定按钮点击监听
* @return {@link BaseDialogConfig}
* @deprecated 请使用 {@link #setOnClickConfirm(View.OnClickListener)}来代替后续版本可能会移除此方法
* @param onClickOk
* @return
*/
public BaseDialogConfig setOnClickOk(View.OnClickListener onClickOk) {
return setOnClickConfirm(onClickOk);
}
/**
* {@link WindowManager.LayoutParams#x}
* @return {@link #x}
*/
public int getX() {
return x;
}
/**
* {@link WindowManager.LayoutParams#x}
* @param x x轴坐标
*/
public BaseDialogConfig setX(int x) {
this.x = x;
return this;
}
/**
* {@link WindowManager.LayoutParams#y}
* @return {@link #y}
*/
public int getY() {
return y;
}
/**
* {@link WindowManager.LayoutParams#y}
* @param y y轴坐标
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setY(int y) {
this.y = y;
return this;
}
/**
* {@link WindowManager.LayoutParams#verticalMargin}
* @return {@link #verticalMargin}
*/
public float getVerticalMargin() {
return verticalMargin;
}
/**
* {@link WindowManager.LayoutParams#verticalMargin}
* @param verticalMargin 垂直边距
*/
public void setVerticalMargin(float verticalMargin) {
this.verticalMargin = verticalMargin;
}
/**
* {@link WindowManager.LayoutParams#horizontalMargin}
* @return {@link #horizontalMargin}
*/
public float getHorizontalMargin() {
return horizontalMargin;
}
/**
* {@link WindowManager.LayoutParams#horizontalMargin}
* @param horizontalMargin 水平边距
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setHorizontalMargin(float horizontalMargin) {
this.horizontalMargin = horizontalMargin;
this.onClickOk = onClickOk;
return this;
}
/**
* {@link WindowManager.LayoutParams#horizontalWeight}
* @return {@link #horizontalWeight}
*/
public float getHorizontalWeight() {
return horizontalWeight;
}
/**
* {@link WindowManager.LayoutParams#horizontalWeight}
* @param horizontalWeight 水平方向权重
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setHorizontalWeight(float horizontalWeight) {
this.horizontalWeight = horizontalWeight;
return this;
}
/**
* {@link WindowManager.LayoutParams#verticalWeight}
* @return {@link #verticalWeight}
*/
public float getVerticalWeight() {
return verticalWeight;
}
/**
* {@link WindowManager.LayoutParams#verticalWeight}
* @param verticalWeight 垂直方向权重
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setVerticalWeight(float verticalWeight) {
this.verticalWeight = verticalWeight;
return this;
}
}

@ -1,7 +1,5 @@
package com.king.app.dialog.fragment;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
@ -11,7 +9,6 @@ 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 {
@ -19,6 +16,7 @@ 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;
@ -59,22 +57,12 @@ public class AppDialogFragment extends BaseDialogFragment {
line.setVisibility(config.isHideCancel() ? View.GONE : View.VISIBLE);
}
Button btnDialogConfirm = rootView.findViewById(config.getConfirmId());
if(btnDialogConfirm != null){
setText(btnDialogConfirm,config.getConfirm());
btnDialogConfirm.setOnClickListener(config.getOnClickConfirm() != null ? config.getOnClickConfirm() : getOnClickDismiss());
Button btnDialogOK = rootView.findViewById(config.getOkId());
if(btnDialogOK != null){
setText(btnDialogOK,config.getOk());
btnDialogOK.setOnClickListener(config.getOnClickOk() != null ? config.getOnClickOk() : getOnClickDismiss());
}
}
}
@Override
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) {
if(config != null){
super.initDialogWindow(context,dialog,config.getGravity(), config.getWidthRatio(), config.getX(), config.getY(), config.getHorizontalMargin(), config.getVerticalMargin(), config.getHorizontalWeight(), config.getVerticalWeight(), config.getAnimationStyleId());
}else{
super.initDialogWindow(context, dialog, gravity, widthRatio, x, y, horizontalMargin, verticalMargin, horizontalWeight, verticalWeight, animationStyleId);
}
}
}

@ -5,7 +5,9 @@ import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.Gravity;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -15,12 +17,7 @@ import android.widget.TextView;
import com.king.app.dialog.R;
import androidx.annotation.NonNull;
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 {
@ -29,78 +26,32 @@ public abstract class BaseDialogFragment extends DialogFragment {
private View mRootView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
super.getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mRootView = inflater.inflate(getRootLayoutId(), container, false);
init(mRootView);
return mRootView;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setCanceledOnTouchOutside(false);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
initDialogWindow(getContext(),dialog,Gravity.NO_GRAVITY,DEFAULT_WIDTH_RATIO,0, 0, 0, 0, 0, 0, R.style.app_dialog_scale_animation);
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);
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
super.getDialog().getWindow().getAttributes().windowAnimations = R.style.app_dialog_animation;
getDialog().setCanceledOnTouchOutside(false);
setDialogWindow(getContext(),getDialog(),DEFAULT_WIDTH_RATIO);
}
/**
* 设置弹框窗口配置
* @param context
* @param dialog
* @param gravity Dialog的对齐方式
* @param widthRatio 宽度比例根据屏幕宽度计算得来
* @param x x轴偏移量需与 gravity 结合使用
* @param y y轴偏移量需与 gravity 结合使用
* @param horizontalMargin 水平方向边距
* @param verticalMargin 垂直方向边距
* @param horizontalWeight 水平方向权重
* @param verticalWeight 垂直方向权重
* @param animationStyleId 动画样式
*/
private void setDialogWindow(Context context,Dialog dialog,int gravity,float widthRatio,int x, int y, float horizontalMargin, float verticalMargin, float horizontalWeight, float verticalWeight, int animationStyleId){
private void setDialogWindow(Context context, Dialog dialog, float widthRatio){
Window window = dialog.getWindow();
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
WindowManager.LayoutParams lp = window.getAttributes();
lp.windowAnimations = animationStyleId;
lp.width = (int)(context.getResources().getDisplayMetrics().widthPixels * widthRatio);
lp.gravity = gravity;
lp.x = x;
lp.y = y;
lp.horizontalMargin = horizontalMargin;
lp.verticalMargin = verticalMargin;
lp.horizontalWeight = horizontalWeight;
lp.verticalWeight = verticalWeight;
window.setAttributes(lp);
}
/**
* 根视图
* @return
*/
protected View getRootView(){
return mRootView;
}
@ -122,16 +73,8 @@ public abstract class BaseDialogFragment extends DialogFragment {
}
};
/**
* 获取根布局ID
* @return 根布局ID
*/
public abstract int getRootLayoutId();
/**
* 初始化
* @param rootView
*/
public abstract void init(View rootView);
}

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="100%"
android:toYDelta="0%" />
</set>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="0%"
android:toYDelta="100%" />
</set>

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale android:duration="200"
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="0.0"

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="-100%"
android:toXDelta="0%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="0%"
android:toXDelta="-100%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale android:duration="200"
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="100%"
android:toXDelta="0%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="0%"
android:toXDelta="100%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:toXScale="1.0"
android:toYScale="1.0"/>
<translate
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="100%"
android:toYDelta="0%" />
</set>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.0"
android:toYScale="0.0"/>
<translate
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="0%"
android:toYDelta="100%" />
</set>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:toXScale="1.0"
android:toYScale="1.0"/>
<translate
android:fromXDelta="-100%"
android:toXDelta="0%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.0"
android:toYScale="0.0"/>
<translate
android:fromXDelta="0%"
android:toXDelta="-100%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:toXScale="1.0"
android:toYScale="1.0"/>
<translate
android:fromXDelta="100%"
android:toXDelta="0%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.0"
android:toYScale="0.0"/>
<translate
android:fromXDelta="0%"
android:toXDelta="100%"
android:fromYDelta="0"
android:toYDelta="0" />
</set>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:toXScale="1.0"
android:toYScale="1.0"/>
<translate
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="-100%"
android:toYDelta="0%" />
</set>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.0"
android:toYScale="0.0"/>
<translate
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="0%"
android:toYDelta="-100%" />
</set>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="-100%"
android:toYDelta="0%" />
</set>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="0%"
android:toYDelta="-100%" />
</set>

@ -11,10 +11,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:padding="10dp"
android:lines="1"
android:textSize="@dimen/app_dialog_title_text_size"
android:textColor="@color/app_dialog_title_color"
@ -23,13 +20,10 @@
android:id="@+id/tvDialogContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:padding="10dp"
android:textSize="@dimen/app_dialog_content_text_size"
android:layout_marginBottom="16dp"
android:textColor="@color/app_dialog_content_color"
android:layout_marginBottom="16dp"
android:lineSpacingMultiplier="1.2" />
<include layout="@layout/app_dialog_line_h"/>
<LinearLayout
@ -52,13 +46,13 @@
android:id="@+id/line"
layout="@layout/app_dialog_line_v"/>
<Button
android:id="@+id/btnDialogConfirm"
android:id="@+id/btnDialogOK"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_weight="1"
android:text="@string/app_dialog_confirm"
android:text="@string/app_dialog_ok"
android:textSize="@dimen/app_dialog_button_text_size"
android:textColor="@color/app_dialog_button_color_selector"
android:background="?android:attr/selectableItemBackground"/>

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorAccent">#5588FF</color>
<color name="colorAccent">#FF4081</color>
<color name="app_dialog_title_color">#333333</color>
<color name="app_dialog_content_color">#333333</color>
<color name="app_dialog_button_normal_color">#333333</color>
<color name="app_dialog_button_pressed_color">@color/colorAccent</color>
<color name="app_dialog_line_color">#F2F2F2</color>
<color name="app_dialog_line_color">#f2f2f2</color>
</resources>

@ -3,7 +3,5 @@
<string name="app_dialog_title">提示</string>
<string name="app_dialog_cancel">取消</string>
<string name="app_dialog_confirm">确定</string>
<string name="app_dialog_ok">确定</string>
</resources>

@ -2,52 +2,9 @@
<resources>
<!-- dialog animation -->
<style name="app_dialog_scale_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_scale_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_scale_out</item>
</style>
<style name="app_dialog_animation" parent="app_dialog_scale_animation">
</style>
<style name="app_dialog_left_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_left_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_left_out</item>
</style>
<style name="app_dialog_top_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_top_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_top_out</item>
</style>
<style name="app_dialog_right_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_right_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_right_out</item>
</style>
<style name="app_dialog_bottom_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_bottom_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_bottom_out</item>
</style>
<style name="app_dialog_scale_left_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_scale_left_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_scale_left_out</item>
</style>
<style name="app_dialog_scale_top_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_scale_top_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_scale_top_out</item>
</style>
<style name="app_dialog_scale_right_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_scale_right_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_scale_right_out</item>
</style>
<style name="app_dialog_scale_bottom_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_scale_bottom_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_scale_bottom_out</item>
<style name="app_dialog_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_out</item>
</style>
<!-- dialog style -->
@ -58,7 +15,7 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowAnimationStyle">@style/app_dialog_scale_animation</item>
<item name="android:windowAnimationStyle">@style/app_dialog_animation</item>
</style>
</resources>

@ -12,7 +12,7 @@ android {
targetSdkVersion build_versions.targetSdk
versionCode app_version.versionCode
versionName app_version.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
@ -31,11 +31,12 @@ android {
}
dependencies {
testImplementation "junit:junit:$versions.junit"
androidTestImplementation "androidx.test.ext:junit:$versions.androidExtJunit"
androidTestImplementation "androidx.test.espresso:espresso-core:$versions.espressoCore"
compileOnly "androidx.appcompat:appcompat:$versions.appcompat"
compileOnly "com.squareup.okhttp3:okhttp:$versions.okhttp"
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation deps.test.junit
androidTestImplementation deps.test.runner
androidTestImplementation deps.test.espresso
//support
compileOnly deps.support.appcompat
compileOnly deps.okhttp
}

@ -1,13 +1,12 @@
package com.king.app.updater;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import static org.junit.Assert.*;
/**

@ -2,8 +2,11 @@
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>
@ -12,7 +15,7 @@
<provider
android:name="com.king.app.updater.provider.AppUpdaterFileProvider"
android:authorities="${applicationId}.AppUpdaterFileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

@ -6,6 +6,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
@ -19,9 +21,6 @@ import com.king.app.updater.util.PermissionUtils;
import java.util.Map;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
@ -84,7 +83,7 @@ public class AppUpdater {
* 开始下载
*/
public void start(){
if(mConfig != null && !TextUtils.isEmpty(mConfig.getUrl())){
if(mConfig!=null && !TextUtils.isEmpty(mConfig.getUrl())){
//如果mContext是Activity,并且配置了下载路径,则默认会校验一次动态权限。
if(mContext instanceof Activity && !TextUtils.isEmpty(mConfig.getPath())){
PermissionUtils.verifyReadAndWritePermissions((Activity) mContext,Constants.RE_CODE_STORAGE_PERMISSION);
@ -96,7 +95,7 @@ public class AppUpdater {
startDownloadService();
}else{
throw new NullPointerException("Url must not be empty.");
throw new NullPointerException("Url = null");
}
}
@ -104,6 +103,7 @@ public class AppUpdater {
* 启动下载服务
*/
private void startDownloadService(){
Intent intent = new Intent(mContext, DownloadService.class);
if(mCallback!=null || mHttpManager!=null){//bindService
mServiceConnection = new ServiceConnection() {
@ -147,7 +147,6 @@ public class AppUpdater {
*/
public static class Builder{
private UpdateConfig mConfig;
public Builder(){
@ -168,7 +167,6 @@ public class AppUpdater {
* 设置保存的路径建议使用默认不做设置
* @param path 下载保存的文件路径
* @return
* @deprecated 因为适配Android Q的分区存储所以此方法已弃用不建议再使用
*/
@Deprecated
public Builder setPath(String path){
@ -198,11 +196,11 @@ public class AppUpdater {
/**
* 设置通知ID
* @param notificationId 通知ID
* @param notifyId 通知ID
* @return
*/
public Builder setNotificationId(int notificationId) {
mConfig.setNotificationId(notificationId);
public Builder setNotificationId(int notifyId) {
mConfig.setNotificationId(notifyId);
return this;
}
@ -269,7 +267,7 @@ public class AppUpdater {
/**
* 设置FileProvider的authority
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".fileProvider"}
* @return
*/
public Builder setAuthority(String authority){
@ -354,26 +352,17 @@ public class AppUpdater {
/**
* 设置是否自动删除取消下载的文件
* @param deleteCancelFile 是否删除取消下载的文件默认为true
*/
public Builder setDeleteCancelFile(boolean deleteCancelFile){
mConfig.setDeleteCancelFile(deleteCancelFile);
return this;
}
/**
* 是否支持通过删除通知栏来取消下载默认为false
* @param cancelDownload
* @param isDeleteCancelFile 是否删除取消下载的文件默认为true
* @return
*/
public Builder setCancelDownload(boolean cancelDownload){
mConfig.setCancelDownload(cancelDownload);
public Builder setDeleteCancelFile(boolean isDeleteCancelFile){
mConfig.setDeleteCancelFile(isDeleteCancelFile);
return this;
}
public AppUpdater build(@NonNull Context context){
return new AppUpdater(context,mConfig);
AppUpdater appUpdater = new AppUpdater(context,mConfig);
return appUpdater;
}
}

@ -4,23 +4,20 @@ package com.king.app.updater;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.DrawableRes;
import com.king.app.updater.constant.Constants;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.DrawableRes;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class UpdateConfig implements Parcelable {
/**
* APK下载的Url
*/
private String mUrl;
/**
* 保存路径
@ -58,7 +55,7 @@ public class UpdateConfig implements Parcelable {
*/
private String mChannelName;
/**
* 默认{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
* 默认{@link Context#getPackageName() + ".fileProvider"}
*/
private String mAuthority;
/**
@ -99,11 +96,6 @@ public class UpdateConfig implements Parcelable {
*/
private boolean isDeleteCancelFile = true;
/**
* 是否支持通过删除通知栏来取消下载
*/
private boolean isCancelDownload = false;
/**
* APK文件的MD5
*/
@ -118,10 +110,6 @@ public class UpdateConfig implements Parcelable {
return mUrl;
}
/**
* 设置APK下载地址
* @param url 下载地址
*/
public void setUrl(String url) {
this.mUrl = url;
}
@ -130,13 +118,6 @@ public class UpdateConfig implements Parcelable {
return mPath;
}
/**
* 设置保存的路径建议使用默认不做设置
* @param path 下载保存的文件路径
* @return
* @deprecated 因为适配Android Q的分区存储所以此方法已弃用不建议再使用
*/
@Deprecated
public void setPath(String path) {
this.mPath = path;
}
@ -145,10 +126,6 @@ public class UpdateConfig implements Parcelable {
return mFilename;
}
/**
* 设置保存的文件名
* @param filename 下载的保存的apk文件名 默认优先取url文件名
*/
public void setFilename(String filename) {
this.mFilename = filename;
}
@ -157,10 +134,6 @@ public class UpdateConfig implements Parcelable {
return isShowNotification;
}
/**
* 设置是否显示通知栏
* @param isShowNotification 是否显示通知栏 默认true
*/
public void setShowNotification(boolean isShowNotification) {
this.isShowNotification = isShowNotification;
}
@ -169,10 +142,6 @@ public class UpdateConfig implements Parcelable {
return mChannelId;
}
/**
* 设置通知渠道ID
* @param channelId 通知渠道ID 默认兼容O
*/
public void setChannelId(String channelId) {
this.mChannelId = channelId;
}
@ -181,17 +150,10 @@ public class UpdateConfig implements Parcelable {
return mChannelName;
}
/**
* 设置通知渠道名称
* @param channelName 通知渠道名称 默认兼容O
*/
public void setChannelName(String channelName) {
this.mChannelName = channelName;
}
/**
* 设置通知ID
* @param notificationId 通知ID
*/
public void setNotificationId(int notificationId){
this.mNotificationId = notificationId;
}
@ -200,11 +162,6 @@ public class UpdateConfig implements Parcelable {
return this.mNotificationId;
}
/**
* 设置通知图标
* @param icon 通知栏图标 默认取App的icon
*/
public void setNotificationIcon(@DrawableRes int icon){
this.mNotificationIcon = icon;
}
@ -217,10 +174,6 @@ public class UpdateConfig implements Parcelable {
return isInstallApk;
}
/**
* 设置下载完成后知否自动触发安装APK
* @param isInstallApk 下载完成后是否自动调用安装APK默认true
*/
public void setInstallApk(boolean isInstallApk) {
this.isInstallApk = isInstallApk;
}
@ -229,10 +182,6 @@ public class UpdateConfig implements Parcelable {
return mAuthority;
}
/**
* 设置FileProvider的authority
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
*/
public void setAuthority(String authority) {
this.mAuthority = authority;
}
@ -241,10 +190,6 @@ public class UpdateConfig implements Parcelable {
return isShowPercentage;
}
/**
* 设置下载时通知栏是否显示下载百分比
* @param showPercentage 下载时通知栏是否显示百分比
*/
public void setShowPercentage(boolean showPercentage) {
isShowPercentage = showPercentage;
}
@ -253,10 +198,6 @@ public class UpdateConfig implements Parcelable {
return isReDownload;
}
/**
* 设置下载失败时是否支持点击通知栏重新下载与之相关联的方法{@link #setReDownloads(int)}
* @param reDownload 下载失败时是否支持点击通知栏重新下载默认true
*/
public void setReDownload(boolean reDownload) {
isReDownload = reDownload;
}
@ -265,10 +206,6 @@ public class UpdateConfig implements Parcelable {
return reDownloads;
}
/**
* 设置下载失败时最多重新下载次数与之相关联的方法{@link #setReDownload(boolean)}
* @param reDownloads 下载失败时是否支持点击通知栏重新下载默认最多重新下载3次
*/
public void setReDownloads(int reDownloads) {
this.reDownloads = reDownloads;
}
@ -277,10 +214,6 @@ public class UpdateConfig implements Parcelable {
return isVibrate;
}
/**
* 设置通知是否震动提示
* @param vibrate 是否震动提示为true时使用通知默认震动Android O(8.0)以上设置只有初次创建channel时有效后续修改属性无效想要重新有效需修改channelId或卸载App重装
*/
public void setVibrate(boolean vibrate) {
isVibrate = vibrate;
}
@ -289,10 +222,6 @@ public class UpdateConfig implements Parcelable {
return isSound;
}
/**
* 设置通知是否铃声提示
* @param sound 是否铃声提示为true时使用通知默认铃声Android O(8.0)以上设置只有初次创建channel时有效后续修改属性无效想要重新有效需修改channelId或卸载App重装
*/
public void setSound(boolean sound) {
isSound = sound;
}
@ -301,13 +230,6 @@ public class UpdateConfig implements Parcelable {
return versionCode;
}
/**
* 设置要下载APK的versionCode用于优先取缓存时通过versionCode校验APK文件是否一致
* 缓存校验目前支持两种方式一种是通过versionCode校验{@link #setVersionCode(Integer)}一种是文件MD5校验{@link #setApkMD5(String)}推荐使用MD5校验方式
* 如果两种方式都设置了则只校验MD5
* @param versionCode 为null表示不处理默认不存在则下载存在则重新下载不为null时表示会优先校验本地是否存在已下载版本号为versionCode的APK
* 如果存在则不会重新下载(AppUpdater会自动校验packageName一致性)直接取本地APK反之重新下载
*/
public void setVersionCode(Integer versionCode){
this.versionCode = versionCode;
}
@ -316,12 +238,7 @@ public class UpdateConfig implements Parcelable {
return mRequestProperty;
}
/**
* 设置APK文件的MD5用于优先取缓存时通过MD5校验文件APK是否一致
* 缓存校验目前支持两种方式一种是通过versionCode校验{@link #setVersionCode(Integer)}一种是文件MD5校验{@link #setApkMD5(String)}推荐使用MD5校验方式
* 如果两种方式都设置了则只校验MD5
* @param md5 为null表示不处理如果设置了MD5则缓存APK的MD5相同时只下载一次优先取本地缓存
*/
public void setApkMD5(String md5){
this.apkMD5 = md5;
}
@ -330,20 +247,11 @@ public class UpdateConfig implements Parcelable {
return apkMD5;
}
/**
* 请求头添加参数
* @param key
* @param value
*/
public void addHeader(String key, String value){
initRequestProperty();
mRequestProperty.put(key,value);
}
/**
* 请求头添加参数
* @param headers
*/
public void addHeader(Map<String,String> headers){
initRequestProperty();
mRequestProperty.putAll(headers);
@ -359,25 +267,10 @@ public class UpdateConfig implements Parcelable {
return isDeleteCancelFile;
}
/**
* 设置是否自动删除取消下载的文件
* @param deleteCancelFile 是否删除取消下载的文件默认为true
*/
public void setDeleteCancelFile(boolean deleteCancelFile) {
isDeleteCancelFile = deleteCancelFile;
}
public boolean isCancelDownload(){
return isCancelDownload;
}
/**
* 是否支持通过删除通知栏来取消下载默认为false
* @param cancelDownload
*/
public void setCancelDownload(boolean cancelDownload) {
isCancelDownload = cancelDownload;
}
@Override
public int describeContents() {
@ -413,7 +306,6 @@ public class UpdateConfig implements Parcelable {
}
dest.writeByte(this.isDeleteCancelFile ? (byte) 1 : (byte) 0);
dest.writeByte(this.isCancelDownload ? (byte) 1 : (byte) 0);
dest.writeString(this.apkMD5);
}
@ -442,7 +334,6 @@ public class UpdateConfig implements Parcelable {
this.mRequestProperty.put(key, value);
}
this.isDeleteCancelFile = in.readByte() != 0;
this.isCancelDownload = in.readByte() != 0;
this.apkMD5 = in.readString();
}

@ -9,7 +9,7 @@ public interface UpdateCallback {
/**
* 最开始调用(在onStart之前调用)
* @param isDownloading true 表示已经在下载false表示当前未开始下载即将开始下载
* @param isDownloading true 表示已经在下载false表示准备刚调用下载
*/
void onDownloading(boolean isDownloading);

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

@ -2,8 +2,10 @@ package com.king.app.updater.http;
import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.Nullable;
import android.util.Log;
import com.king.app.updater.util.LogUtils;
import com.king.app.updater.constant.Constants;
import com.king.app.updater.util.SSLSocketFactoryUtils;
import java.io.File;
@ -16,10 +18,8 @@ import java.util.Map;
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 {
@ -31,7 +31,7 @@ public class HttpManager implements IHttpManager {
private int mTimeout;
private DownloadTask mDownloadTask;
private boolean isCancel;
private static volatile HttpManager INSTANCE;
@ -60,21 +60,19 @@ public class HttpManager implements IHttpManager {
@Override
public void download(String url, String path, String filename, @Nullable Map<String,String> requestProperty, DownloadCallback callback) {
mDownloadTask = new DownloadTask(url, path, filename, mTimeout, requestProperty, callback);
mDownloadTask.execute();
isCancel = false;
new DownloadTask(url,path,filename,requestProperty,callback).execute();
}
@Override
public void cancel() {
if(mDownloadTask != null){
mDownloadTask.isCancel = true;
}
isCancel = true;
}
/**
* 异步下载任务
*/
private static class DownloadTask extends AsyncTask<Void,Long,File> {
private class DownloadTask extends AsyncTask<Void,Long,File> {
private String url;
@ -88,96 +86,92 @@ public class HttpManager implements IHttpManager {
private Exception exception;
private int timeout;
private volatile boolean isCancel;
public DownloadTask(String url, String path, String filename, int timeout, @Nullable Map<String,String> requestProperty, DownloadCallback callback){
public DownloadTask(String url, String path, String filename ,@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{
HttpURLConnection connect = (HttpURLConnection)new URL(url).openConnection();
connect.setRequestMethod("GET");
connect.setRequestProperty("Accept-Encoding", "identity");
HttpURLConnection connect = (HttpURLConnection)new URL(url).openConnection();
connect.setRequestMethod("GET");
connect.setRequestProperty("Accept-Encoding", "identity");
connect.setReadTimeout(timeout);
connect.setConnectTimeout(timeout);
connect.setReadTimeout(mTimeout);
connect.setConnectTimeout(mTimeout);
if(requestProperty != null){
for(Map.Entry<String,String> entry : requestProperty.entrySet()){
connect.setRequestProperty(entry.getKey(),entry.getValue());
if(requestProperty != null){
for(Map.Entry<String,String> entry : requestProperty.entrySet()){
connect.setRequestProperty(entry.getKey(),entry.getValue());
}
}
}
connect.connect();
connect.connect();
LogUtils.d("Content-Type:" + connect.getContentType());
int responseCode = connect.getResponseCode();
switch (responseCode){
case HttpURLConnection.HTTP_OK: {
InputStream is = connect.getInputStream();
Log.d(Constants.TAG,"Content-Type:" + connect.getContentType());
int responseCode = connect.getResponseCode();
switch (responseCode){
case HttpURLConnection.HTTP_OK: {
InputStream is = connect.getInputStream();
long length = connect.getContentLength();
long length = connect.getContentLength();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
length = connect.getContentLengthLong();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
length = connect.getContentLengthLong();
}
LogUtils.d( "contentLength:" + length);
Log.d(Constants.TAG, "contentLength:" + length);
long progress = 0;
byte[] buffer = new byte[8192];
int len;
File file = new File(path, filename);
FileOutputStream fos = new FileOutputStream(file);
while ((len = is.read(buffer)) != -1) {
if (isCancel) {
cancel(true);
break;
}
fos.write(buffer, 0, len);
progress += len;
//更新进度
if (length > 0) {
publishProgress(progress, length);
}
}
long progress = 0;
fos.flush();
fos.close();
is.close();
byte[] buffer = new byte[8192];
connect.disconnect();
int len;
File file = new File(path, filename);
FileOutputStream fos = new FileOutputStream(file);
while ((len = is.read(buffer)) != -1) {
if (isCancel) {
cancel(true);
break;
if(progress <= 0 && length <= 0){
throw new IllegalStateException(String.format("contentLength = %d",length));
}
fos.write(buffer, 0, len);
progress += len;
// 更新进度
if (length > 0) {
publishProgress(progress, length);
}
}
fos.flush();
fos.close();
is.close();
connect.disconnect();
if(progress <= 0 && length <= 0){
throw new IllegalStateException(String.format("contentLength = %d",length));
return file;
}
case HttpURLConnection.HTTP_MULT_CHOICE:
case HttpURLConnection.HTTP_MOVED_PERM:
case HttpURLConnection.HTTP_MOVED_TEMP:
case HttpURLConnection.HTTP_SEE_OTHER:
case HTTP_TEMP_REDIRECT:
case HTTP_PERM_REDIRECT: {//重定向
String redirectUrl = connect.getHeaderField("Location");
Log.d(Constants.TAG,"redirectUrl = " + redirectUrl);
connect.disconnect();
return download(redirectUrl);
}
default://连接失败
throw new ConnectException(String.format("responseCode = %d",responseCode));
return file;
}
case HttpURLConnection.HTTP_MULT_CHOICE:
case HttpURLConnection.HTTP_MOVED_PERM:
case HttpURLConnection.HTTP_MOVED_TEMP:
case HttpURLConnection.HTTP_SEE_OTHER:
case HTTP_TEMP_REDIRECT:
case HTTP_PERM_REDIRECT: {// 重定向
String redirectUrl = connect.getHeaderField("Location");
LogUtils.d("redirectUrl = " + redirectUrl);
connect.disconnect();
return download(redirectUrl);
}
default://连接失败
throw new ConnectException(String.format("responseCode = %d",responseCode));
}
}
@Override
@ -235,6 +229,7 @@ public class HttpManager implements IHttpManager {
}
}
}
}

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

@ -1,25 +0,0 @@
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);
}

@ -1,38 +0,0 @@
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);
}
}

@ -1,7 +1,6 @@
package com.king.app.updater.provider;
import androidx.core.content.FileProvider;
import android.support.v4.content.FileProvider;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>

@ -6,7 +6,10 @@ import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;
import com.king.app.updater.R;
import com.king.app.updater.UpdateConfig;
@ -14,22 +17,13 @@ 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.LogUtils;
import com.king.app.updater.util.NotificationUtils;
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 {
@ -40,15 +34,21 @@ public class DownloadService extends Service {
*/
private boolean isDownloading;
/**
* 失败后重新下载次数
* 最后更新进度用来降频刷新
*/
private int mCount = 0;
private int mLastProgress = -1;
/**
* 最后进度更新时间用来降频刷新
*/
private long mLastTime;
/**
* Http管理器
* 失败后重新下载次数
*/
private int mCount = 0;
private IHttpManager mHttpManager;
private File mApkFile;
private File mFile;
private Context getContext(){
return this;
@ -56,7 +56,9 @@ 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();
@ -67,10 +69,10 @@ public class DownloadService extends Service {
mCount++;
}
//获取配置信息
UpdateConfig config = intent.getParcelableExtra(Constants.KEY_UPDATE_CONFIG);
startDownload(config,null,null, new NotificationImpl());
UpdateConfig config = intent.getParcelableExtra(Constants.KEY_UPDATE_CONFIG);
startDownload(config,null,null);
}else{
LogUtils.w("Please do not duplicate downloads.");
Log.w(Constants.TAG,"Please do not repeat the download.");
}
}
@ -87,13 +89,18 @@ public class DownloadService extends Service {
* @param httpManager
* @param callback
*/
public void startDownload(@NonNull UpdateConfig config,@Nullable IHttpManager httpManager,@Nullable UpdateCallback callback, @Nullable INotification notification){
if(callback != null){
public void startDownload(UpdateConfig config,IHttpManager httpManager,UpdateCallback callback){
if(config == null){
return;
}
if(callback!=null){
callback.onDownloading(isDownloading);
}
if(isDownloading){
LogUtils.w("Please do not duplicate downloads.");
Log.w(Constants.TAG,"Please do not repeat the download.");
return;
}
@ -103,7 +110,7 @@ public class DownloadService extends Service {
//如果保存路径为空则使用缓存路径
if(TextUtils.isEmpty(path)){
path = getCacheFilesDir(getContext());
path = getExternalFilesDir(getContext());
}
File dirFile = new File(path);
if(!dirFile.exists()){
@ -115,45 +122,51 @@ public class DownloadService extends Service {
filename = AppUtils.getAppFullName(getContext(),url,getResources().getString(R.string.app_name));
}
mApkFile = new File(path, filename);
if(mApkFile.exists()){//文件是否存在
mFile = new File(path,filename);
if(mFile.exists()){//文件是否存在
Integer versionCode = config.getVersionCode();
String apkMD5 = config.getApkMD5();
//是否存在相同的apk
boolean isExistApk = false;
if(!TextUtils.isEmpty(apkMD5)){//如果存在MD5,则优先校验MD5
LogUtils.d(String.format(Locale.getDefault(),"UpdateConfig.apkMD5:%s",apkMD5));
isExistApk = AppUtils.checkFileMD5(mApkFile,apkMD5);
}else if(versionCode != null){//如果存在versionCode,则校验versionCode
LogUtils.d(String.format(Locale.getDefault(),"UpdateConfig.versionCode:%d",versionCode));
isExistApk = AppUtils.apkExists(getContext(),versionCode,mApkFile);
isExistApk = AppUtils.checkFileMD5(mFile,apkMD5);
}else if(versionCode!=null){//如果存在versionCode,则校验versionCode
try{
isExistApk = AppUtils.apkExists(getContext(),versionCode,mFile);
}catch (Exception e){
Log.w(Constants.TAG,e);
}
}
if(isExistApk){
//本地已经存在要下载的APK
LogUtils.d("CacheFile:" + mApkFile);
Log.d(Constants.TAG,"CacheFile:" + mFile);
if(config.isInstallApk()){
String authority = config.getAuthority();
if(TextUtils.isEmpty(authority)){//如果为空则默认
authority = AppUtils.getFileProviderAuthority(getContext());
authority = getContext().getPackageName() + Constants.DEFAULT_FILE_PROVIDER;
}
AppUtils.installApk(getContext(), mApkFile, authority);
AppUtils.installApk(getContext(),mFile,authority);
}
if(callback != null){
callback.onFinish(mApkFile);
if(callback!=null){
callback.onFinish(mFile);
}
stopService();
return;
}
//删除旧文件
mApkFile.delete();
mFile.delete();
}
LogUtils.d("File:" + mApkFile);
Log.d(Constants.TAG,"File:" + mFile);
mHttpManager = httpManager != null ? httpManager : HttpManager.getInstance();
IHttpManager.DownloadCallback downloadCallback = new AppDownloadCallback(getContext(),this, config, mApkFile, callback, notification);
mHttpManager.download(url,path,filename,config.getRequestProperty(), downloadCallback);
if(httpManager != null){
mHttpManager = httpManager;
}else{
mHttpManager = HttpManager.getInstance();
}
mHttpManager.download(url,path,filename,config.getRequestProperty(),new AppDownloadCallback(config,callback));
}
@ -161,7 +174,7 @@ public class DownloadService extends Service {
* 停止下载
*/
public void stopDownload(){
if(mHttpManager != null){
if(mHttpManager!=null){
mHttpManager.cancel();
}
}
@ -171,18 +184,12 @@ public class DownloadService extends Service {
* @param context
* @return
*/
private String getCacheFilesDir(Context context) {
File[] files = ContextCompat.getExternalFilesDirs(context, Constants.DEFAULT_DIR);
if(files != null && files.length > 0){
private String getExternalFilesDir(Context context) {
File[] files = ContextCompat.getExternalFilesDirs(context,Constants.DEFAULT_DIR);
if(files!=null && files.length > 0){
return files[0].getAbsolutePath();
}
File externalFilesDir = context.getExternalFilesDir(Constants.DEFAULT_DIR);
if(externalFilesDir != null){
return externalFilesDir.getAbsolutePath();
}
return new File(context.getFilesDir(), Constants.DEFAULT_DIR).getAbsolutePath();
return context.getExternalFilesDir(Constants.DEFAULT_DIR).getAbsolutePath();
}
@ -197,14 +204,7 @@ public class DownloadService extends Service {
//---------------------------------------- DownloadCallback
/**
* App下载回调接口
*/
public static class AppDownloadCallback implements IHttpManager.DownloadCallback {
private Context context;
private DownloadService downloadService;
public class AppDownloadCallback implements IHttpManager.DownloadCallback{
public UpdateConfig config;
@ -228,42 +228,24 @@ public class DownloadService extends Service {
private boolean isDeleteCancelFile;
private boolean isCancelDownload;
private 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;
private int reDownloads;
private AppDownloadCallback(UpdateConfig config,UpdateCallback callback){
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(context);
this.notificationIcon = AppUtils.getAppIcon(getContext());
}else{
this.notificationIcon = config.getNotificationIcon();
}
@ -272,113 +254,103 @@ public class DownloadService extends Service {
this.authority = config.getAuthority();
if(TextUtils.isEmpty(config.getAuthority())){//如果为空则默认
authority = AppUtils.getFileProviderAuthority(context);
authority = getContext().getPackageName() + Constants.DEFAULT_FILE_PROVIDER;
}
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) {
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);
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());
}
if(callback != null){
if(callback!=null){
callback.onStart(url);
}
}
@Override
public void onProgress(long progress, long total) {
boolean isChange = false;
long curTime = System.currentTimeMillis();
if(lastTime + 200 < curTime || progress == total) {//降低更新频率
lastTime = curTime;
if(mLastTime + 200 < curTime) {//降低更新频率
mLastTime = curTime;
int currProgress = Math.round(progress * 1.0f / total * 100.0f);
if(currProgress != lastProgress){//百分比改变了才更新
if(currProgress != mLastProgress){//百分比改变了才更新
isChange = true;
lastProgress = currProgress;
String percentage = currProgress + "%";
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(isShowNotification) {
mLastProgress = currProgress;
String content = getString(R.string.app_updater_progress_notification_content);
if (isShowPercentage) {
content += percentage;
}
notification.onProgress(context,notifyId, channelId, notificationIcon, context.getString(R.string.app_updater_progress_notification_title), content, currProgress, 100,isCancelDownload);
NotificationUtils.showProgressNotification(getContext(),notifyId, channelId, notificationIcon, getString(R.string.app_updater_progress_notification_title), content, currProgress, 100);
}
}
}
if(callback != null){
if(callback!=null){
callback.onProgress(progress,total,isChange);
}
}
@Override
public void onFinish(File file) {
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);
}
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);
if(isInstallApk){
AppUtils.installApk(context,file,authority);
AppUtils.installApk(getContext(),file,authority);
}
if(callback != null){
if(callback!=null){
callback.onFinish(file);
}
downloadService.stopService();
stopService();
}
@Override
public void onError(Exception e) {
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);
notification.onError(context,notifyId,channelId,notificationIcon,getString(R.string.app_updater_error_notification_title),content,isReDownload,config);
}
Log.w(Constants.TAG,"onError:"+ e.getMessage());
isDownloading = false;
//支持下载失败时重新下载,当重新下载次数不超过限制时才被允许
boolean isReDownload = this.isReDownload && mCount < reDownloads;
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);
if(callback != null){
if(callback!=null){
callback.onError(e);
}
if(!isReDownload){
downloadService.stopService();
stopService();
}
}
@Override
public void onCancel() {
LogUtils.d("Cancel download.");
downloadService.isDownloading = false;
if(isShowNotification && notification != null){
notification.onCancel(context,notifyId);
}
if(callback != null){
Log.d(Constants.TAG,"onCancel");
isDownloading = false;
NotificationUtils.cancelNotification(getContext(),notifyId);
if(callback!=null){
callback.onCancel();
}
if(isDeleteCancelFile && apkFile != null){
apkFile.delete();
if(isDeleteCancelFile && mFile!=null){
mFile.delete();
}
downloadService.stopService();
}
private String getString(@StringRes int resId){
return context.getString(resId);
stopService();
}
}
@ -398,47 +370,22 @@ public class DownloadService extends Service {
}
/**
* 提供绑定服务的方式进行下载
* 提供绑定服务的方式下载
*/
public class DownloadBinder extends Binder {
/**
* 开始下载
* @param config {@link UpdateConfig}
*/
public void start(@NonNull UpdateConfig config){
public void start(UpdateConfig config){
start(config,null);
}
/**
* 开始下载
* @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,UpdateCallback callback){
start(config,null,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());
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}
* @param notification {@link INotification}
*/
public void start(@NonNull UpdateConfig config, @Nullable IHttpManager httpManager, @Nullable UpdateCallback callback,@NonNull INotification notification){
startDownload(config, httpManager, callback, notification);
}
}

@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.Build;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import android.util.Log;
@ -21,9 +22,6 @@ import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import androidx.core.content.FileProvider;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
@ -40,20 +38,15 @@ public final class AppUtils {
*/
public static String getAppFullName(Context context,String url,String defaultName){
if(url.endsWith(".apk")){
String apkName = url.substring(url.lastIndexOf("/") + 1);
if(apkName.length() <= 64){
return apkName;
}
return url.substring(url.lastIndexOf("/") + 1);
}
String filename = getAppName(context);
Log.d(Constants.TAG, "AppName:" + filename);
if(TextUtils.isEmpty(filename)){
filename = defaultName;
}
if(filename.endsWith(".apk")){
return filename;
}
return String.format("%s.apk",filename);
}
@ -65,7 +58,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 +68,7 @@ public final class AppUtils {
* @param archiveFilePath
* @return
*/
public static PackageInfo getPackageInfo(Context context, String archiveFilePath) {
public static PackageInfo getPackageInfo(Context context, String archiveFilePath) throws Exception {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(archiveFilePath, PackageManager.GET_ACTIVITIES);
return packageInfo;
@ -87,6 +80,7 @@ public final class AppUtils {
*/
public static String getAppName(Context context) {
try{
int labelRes = getPackageInfo(context).applicationInfo.labelRes;
return context.getResources().getString(labelRes);
} catch (Exception e) {
@ -106,6 +100,7 @@ public final class AppUtils {
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
@ -127,6 +122,7 @@ 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);
@ -150,17 +146,14 @@ public final class AppUtils {
* @return
* @throws Exception
*/
public static boolean apkExists(Context context,int versionCode,File file){
if(file != null && file.exists()){
public static boolean apkExists(Context context,int versionCode,File file) throws Exception{
if(file!=null && file.exists()){
String packageName = context.getPackageName();
PackageInfo packageInfo = AppUtils.getPackageInfo(context,file.getAbsolutePath());
if(packageInfo != null){// 比对versionCode
Log.d(Constants.TAG,String.format("ApkVersionCode:%d",packageInfo.versionCode));
if(versionCode == packageInfo.versionCode){
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
if(applicationInfo != null && packageName.equals(applicationInfo.packageName)){//比对packageName
return true;
}
if(packageInfo!=null && versionCode == packageInfo.versionCode){//比对versionCode
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
if(applicationInfo!=null && packageName.equals(applicationInfo.packageName)){//比对packageName
return true;
}
}
}
@ -242,10 +235,6 @@ public final class AppUtils {
return null;
}
public static String getFileProviderAuthority(Context context){
return context.getPackageName() + Constants.DEFAULT_FILE_PROVIDER;
}
/**
* 关闭
* @param descriptor

@ -1,316 +0,0 @@
/*
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);
}
}

@ -7,6 +7,9 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.DrawableRes;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
import com.king.app.updater.UpdateConfig;
import com.king.app.updater.constant.Constants;
@ -14,10 +17,6 @@ import com.king.app.updater.service.DownloadService;
import java.io.File;
import androidx.annotation.DrawableRes;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
@ -28,7 +27,7 @@ public class NotificationUtils {
}
/**
* 显示开始下载的通知
* 显示开始下载的通知
* @param notifyId
* @param channelId
* @param channelName
@ -36,12 +35,12 @@ public class NotificationUtils {
* @param title
* @param content
*/
public static void showStartNotification(Context context, int notifyId,String channelId, String channelName,@DrawableRes int icon,CharSequence title,CharSequence content,boolean isVibrate,boolean isSound, boolean isCancelDownload){
public static void showStartNotification(Context context, int notifyId,String channelId, String channelName,@DrawableRes int icon,CharSequence title,CharSequence content,boolean isVibrate,boolean isSound){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createNotificationChannel(context,channelId,channelName,isVibrate,isSound);
}
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
builder.setPriority(NotificationManager.IMPORTANCE_HIGH);
if(isVibrate && isSound){
builder.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND);
}else if(isVibrate){
@ -50,20 +49,8 @@ public class NotificationUtils {
builder.setDefaults(Notification.DEFAULT_SOUND);
}
if(isCancelDownload){
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,true);
PendingIntent deleteIntent = PendingIntent.getService(context, notifyId,intent, getPendingIntentFlags(PendingIntent.FLAG_CANCEL_CURRENT));
builder.setDeleteIntent(deleteIntent);
}
Notification notification = builder.build();
if(isCancelDownload){
notification.flags = Notification.FLAG_ONLY_ALERT_ONCE;
}else{
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONLY_ALERT_ONCE;
}
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONLY_ALERT_ONCE;
notifyNotification(context,notifyId,notification);
}
@ -77,24 +64,11 @@ public class NotificationUtils {
* @param progress
* @param size
*/
public static void showProgressNotification(Context context, int notifyId,String channelId,@DrawableRes int icon,CharSequence title,CharSequence content,int progress,int size, boolean isCancelDownload){
public static void showProgressNotification(Context context, int notifyId,String channelId,@DrawableRes int icon,CharSequence title,CharSequence content,int progress,int size){
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content,progress,size);
if(isCancelDownload){
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,true);
PendingIntent deleteIntent = PendingIntent.getService(context, notifyId, intent, getPendingIntentFlags(PendingIntent.FLAG_CANCEL_CURRENT));
builder.setDeleteIntent(deleteIntent);
}
Notification notification = builder.build();
if(isCancelDownload){
notification.flags = Notification.FLAG_ONLY_ALERT_ONCE;
}else{
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONLY_ALERT_ONCE;
}
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONLY_ALERT_ONCE;
notifyNotification(context,notifyId,notification);
}
@ -112,7 +86,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, getPendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT));
PendingIntent clickIntent = PendingIntent.getActivity(context, notifyId,intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(clickIntent);
Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
@ -133,15 +107,14 @@ 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);
int flag = getPendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT);
if(isReDownload){// 重新下载
Intent intent = new Intent(context, DownloadService.class);
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, flag);
PendingIntent clickIntent = PendingIntent.getService(context, notifyId,intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(clickIntent);
}else{
PendingIntent clickIntent = PendingIntent.getService(context, notifyId, new Intent(), flag);
PendingIntent clickIntent = PendingIntent.getService(context, notifyId,new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(clickIntent);
}
@ -230,7 +203,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);
}
@ -246,15 +219,4 @@ 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;
}
}

@ -8,61 +8,62 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public final class PermissionUtils {
private PermissionUtils() {
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE};
private PermissionUtils(){
throw new AssertionError();
}
/**
* 校验权限
*
* @param activity
* @param requestCode
* @return
*/
public 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;
}
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;
}
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,18 +1,17 @@
package com.king.app.updater.util;
import android.content.Context;
import android.text.TextUtils;
import androidx.annotation.RawRes;
import android.support.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;
@ -22,15 +21,12 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public final class SSLSocketFactoryUtils {
private static final String[] VERIFY_HOST_NAME = new String[]{};
private SSLSocketFactoryUtils() {
private SSLSocketFactoryUtils(){
throw new AssertionError();
}
@ -38,7 +34,7 @@ public final class SSLSocketFactoryUtils {
SSLSocketFactory sslSocketFactory = null;
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, getTrustAllManager(), new SecureRandom());
sslContext.init(null, new TrustManager[]{createTrustAllManager()}, new SecureRandom());
sslSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
@ -49,7 +45,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
@ -70,55 +66,66 @@ 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) {
if (TextUtils.isEmpty(hostname)) {
return false;
}
return !Arrays.asList(VERIFY_HOST_NAME).contains(hostname);
return true;
}
}
/**
*
* @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 sSLSocketFactory = null;
if (sSLSocketFactory == null) {
SSLSocketFactory mSSLSocketFactory = null;
if(mSSLSocketFactory==null){
synchronized (SSLSocketFactoryUtils.class) {
if (sSLSocketFactory == null) {
if(mSSLSocketFactory==null){
SSLContext sslContext = null;
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, getTrustManager(certificates), new SecureRandom());
sSLSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
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) {
e.printStackTrace();
}
//获得sslSocketFactory
mSSLSocketFactory = sslContext.getSocketFactory();
}
}
}
return sSLSocketFactory;
return mSSLSocketFactory;
}
/**
* 获得指定流中的服务器端证书库
*
* @param certificates
* @return
*/
@ -126,7 +133,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) {
@ -135,12 +142,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
@ -159,9 +166,9 @@ public final class SSLSocketFactoryUtils {
/**
* 获得信任所有服务器端证书库
*/
* */
public static TrustManager[] getTrustAllManager() {
return new TrustManager[]{createTrustAllManager()};
return new X509TrustManager[] {createTrustAllManager()};
}

@ -6,5 +6,4 @@
<external-files-path name="app_external_files_path" path="/"/>
<files-path name="app_files_path" path="/"/>
<cache-path name="app_cache_path" path="/"/>
</paths>

@ -10,7 +10,7 @@ android {
targetSdkVersion build_versions.targetSdk
versionCode app_version.versionCode
versionName app_version.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
@ -24,14 +24,13 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation "junit:junit:$versions.junit"
androidTestImplementation "androidx.test.ext:junit:$versions.androidExtJunit"
androidTestImplementation "androidx.test.espresso:espresso-core:$versions.espressoCore"
implementation "androidx.appcompat:appcompat:$versions.appcompat"
implementation "androidx.constraintlayout:constraintlayout:$versions.constraintLayout"
implementation "com.squareup.okhttp3:okhttp:$versions.okhttp"
testImplementation deps.test.junit
androidTestImplementation deps.test.runner
androidTestImplementation deps.test.espresso
//support
implementation deps.support.appcompat
implementation deps.support.constraintlayout
implementation deps.okhttp
implementation project(':app-updater')
implementation project(':app-dialog')

Binary file not shown.

@ -1,18 +0,0 @@
{
"version": 2,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.king.appupdater",
"variantName": "processReleaseResources",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 22,
"versionName": "1.1.3",
"outputFile": "app-release.apk"
}
]
}

@ -0,0 +1 @@
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":17,"versionName":"1.0.10","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]

@ -1,13 +1,12 @@
package com.king.appupdater;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import static org.junit.Assert.*;
/**

@ -2,9 +2,6 @@
<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"

@ -3,10 +3,11 @@ package com.king.appupdater;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.Gravity;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
@ -17,13 +18,12 @@ 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;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
@ -31,11 +31,10 @@ public class MainActivity extends AppCompatActivity {
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";
private TextView tvProgress;
private ProgressBar progressBar;
private Toast toast;
@ -46,11 +45,11 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// progressBar = findViewById(R.id.progressBar);
// progressBar.setVisibility(View.INVISIBLE);
// progressBar.setMax(100);
progressBar = findViewById(R.id.progressBar);
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(){
@ -92,62 +91,41 @@ public class MainActivity extends AppCompatActivity {
public void onDownloading(boolean isDownloading) {
if(isDownloading){
showToast("已经在下载中,请勿重复下载。");
}else{
// showToast("开始下载…");
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_progress,null);
tvProgress = view.findViewById(R.id.tvProgress);
progressBar = view.findViewById(R.id.progressBar);
AppDialog.INSTANCE.showDialog(getContext(),view,false);
}
}
@Override
public void onStart(String url) {
updateProgress(0,100);
progressBar.setProgress(0);
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void onProgress(long progress, long total, boolean isChange) {
if(isChange){
updateProgress(progress,total);
int currProgress = Math.round(progress * 1.0f / total * 100.0f);
progressBar.setProgress(currProgress);
}
}
@Override
public void onFinish(File file) {
AppDialog.INSTANCE.dismissDialog();
showToast("下载完成");
progressBar.setVisibility(View.INVISIBLE);
}
@Override
public void onError(Exception e) {
AppDialog.INSTANCE.dismissDialog();
showToast("下载失败");
progressBar.setVisibility(View.INVISIBLE);
}
@Override
public void onCancel() {
AppDialog.INSTANCE.dismissDialog();
showToast("取消下载");
progressBar.setVisibility(View.INVISIBLE);
}
});
mAppUpdater.start();
}
private void updateProgress(long progress, long total){
if(tvProgress == null || progressBar == null){
return;
}
if(progress > 0){
int currProgress = (int)(progress * 1.0f / total * 100.0f);
tvProgress.setText(getString(R.string.app_updater_progress_notification_content) + currProgress + "%");
progressBar.setProgress(currProgress);
}else{
tvProgress.setText(getString(R.string.app_updater_start_notification_content));
}
}
/**
* 系统弹框升级
*/
@ -162,29 +140,6 @@ public class MainActivity extends AppCompatActivity {
.setUrl(mUrl)
.build(getContext())
.setUpdateCallback(new AppUpdateCallback() {
@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)
.setGravity(Gravity.TOP);
AppDialog.INSTANCE.showDialog(getContext(),config);
new CountDownTimer(2000,500){
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
AppDialog.INSTANCE.dismissDialog();
}
}.start();
}
@Override
public void onProgress(long progress, long total, boolean isChange) {
@ -206,9 +161,9 @@ public class MainActivity extends AppCompatActivity {
private void clickBtn4(){
AppDialogConfig config = new AppDialogConfig(getContext());
config.setTitle("简单弹框升级")
.setConfirm("升级")
.setOk("升级")
.setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
.setOnClickConfirm(new View.OnClickListener() {
.setOnClickOk(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAppUpdater = new AppUpdater(getContext(),mUrl);
@ -216,7 +171,7 @@ public class MainActivity extends AppCompatActivity {
AppDialog.INSTANCE.dismissDialog();
}
});
AppDialog.INSTANCE.showDialog(config);
AppDialog.INSTANCE.showDialog(getContext(),config);
}
/**
@ -224,11 +179,11 @@ public class MainActivity extends AppCompatActivity {
*/
private void clickBtn5(){
AppDialogConfig config = new AppDialogConfig(getContext(),R.layout.dialog);
config.setConfirm("升级")
config.setOk("升级")
.setHideCancel(true)
.setTitle("简单自定义弹框升级")
.setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
.setOnClickConfirm(new View.OnClickListener() {
.setOnClickOk(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAppUpdater = new AppUpdater.Builder()
@ -238,7 +193,8 @@ public class MainActivity extends AppCompatActivity {
AppDialog.INSTANCE.dismissDialog();
}
});
AppDialog.INSTANCE.showDialog(config);
//强制升级,拦截返回
AppDialog.INSTANCE.showDialog(getContext(),AppDialog.INSTANCE.createAppDialogView(getContext(),config),false);
}
/**
@ -252,21 +208,23 @@ public class MainActivity extends AppCompatActivity {
TextView tvContent = view.findViewById(R.id.tvContent);
tvContent.setText("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、");
View btnCancel = view.findViewById(R.id.btnCancel);
Button btnCancel = view.findViewById(R.id.btnCancel);
btnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AppDialog.INSTANCE.dismissDialog();
}
});
View btnConfirm = view.findViewById(R.id.btnConfirm);
btnConfirm.setOnClickListener(new View.OnClickListener() {
Button btnOK = view.findViewById(R.id.btnOK);
btnOK.setOnClickListener(new View.OnClickListener() {
@Override
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校验的方式
// .setPath(Environment.getExternalStorageDirectory() + "/.AppUpdater")//如果适配Android Q,则Environment.getExternalStorageDirectory()将废弃
// .setPath(getExternalFilesDir(Constants.DEFAULT_DIR).getAbsolutePath())//自定义路径,推荐使用默认
// .setApkMD5("3df5b1c1d2bbd01b4a7ddb3f2722ccca")//支持MD5校验,如果缓存APK的MD5与此MD5相同,则直接取本地缓存安装,推荐使用MD5校验的方式
.setVersionCode(BuildConfig.VERSION_CODE)//支持versionCode校验,设置versionCode之后,新版本versionCode相同的apk只下载一次,优先取本地缓存,推荐使用MD5校验的方式
.setFilename("AppUpdater.apk")
.setVibrate(true)
.build(getContext());
@ -284,9 +242,9 @@ public class MainActivity extends AppCompatActivity {
private void clickBtn7(){
AppDialogConfig config = new AppDialogConfig(getContext());
config.setTitle("简单DialogFragment升级")
.setConfirm("升级")
.setOk("升级")
.setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
.setOnClickConfirm(new View.OnClickListener() {
.setOnClickOk(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAppUpdater = new AppUpdater.Builder()

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@color/text_666"/>
<item android:state_pressed="false" android:color="@color/text_999"/>
</selector>

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<shape>
<solid android:color="@color/colorAccent"/>
<corners android:radius="20dp"/>
</shape>
</item>
<item android:state_pressed="true">
<shape>
<solid android:color="@color/colorPrimaryDark"/>
<corners android:radius="20dp"/>
</shape>
</item>
</selector>

@ -1,11 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
@ -14,6 +24,7 @@
android:layout_marginRight="20dp"
android:layout_marginTop="40dp"
android:text="简单一键后台升级"
app:layout_constraintTop_toBottomOf="@+id/progressBar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -25,7 +36,7 @@
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="10dp"
android:text="一键监听进度升级"
android:text="一键监听进度后台升级"
app:layout_constraintTop_toBottomOf="@+id/btn1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
@ -106,4 +117,4 @@
app:layout_constraintRight_toRightOf="parent"
style="@style/OnClick"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</android.support.constraint.ConstraintLayout>

@ -20,7 +20,7 @@
android:id="@+id/tvDialogContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:padding="10dp"
android:textSize="@dimen/app_dialog_content_text_size"
android:textColor="@color/app_dialog_content_color"
android:layout_marginBottom="16dp"
@ -46,13 +46,13 @@
android:id="@+id/line"
layout="@layout/app_dialog_line_v"/>
<Button
android:id="@+id/btnDialogConfirm"
android:id="@+id/btnDialogOK"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_weight="1"
android:text="@string/app_dialog_confirm"
android:text="@string/app_dialog_ok"
android:textSize="18sp"
android:textColor="@color/app_dialog_button_color_selector"
android:background="?android:attr/selectableItemBackground"/>

@ -4,7 +4,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="30dp"
android:background="@drawable/app_dialog_bg"
android:background="@android:color/white"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
@ -13,48 +13,43 @@
android:gravity="center"
android:padding="6dp"
android:lines="1"
android:textSize="16sp" />
android:textSize="16sp"
android:background="#F5F5F5" />
<TextView
android:id="@+id/tvContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:padding="10dp"
android:layout_marginBottom="6dp"
android:lineSpacingMultiplier="1" />
<include layout="@layout/app_dialog_line_h"/>
<LinearLayout
android:orientation="vertical"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="10dp"
android:gravity="center_horizontal">
<TextView
android:id="@+id/btnConfirm"
android:layout_width="match_parent"
<Button
android:id="@+id/btnCancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:gravity="center"
android:layout_weight="1"
android:text="升级"
android:text="@string/app_dialog_cancel"
android:textSize="16sp"
android:textColor="@color/white"
android:background="@drawable/btn_confirm_selector"/>
<TextView
android:id="@+id/btnCancel"
android:layout_width="wrap_content"
android:textColor="@color/app_dialog_button_color_selector"
android:background="?android:attr/selectableItemBackground"/>
<Button
android:id="@+id/btnOK"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center"
android:layout_weight="1"
android:text="@string/ignore"
android:text="升级"
android:textSize="16sp"
android:textColor="@color/btn_cancel_color_selector" />
android:textColor="@color/app_dialog_button_color_selector"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
</LinearLayout>

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:gravity="center_vertical"
android:background="@drawable/app_dialog_bg">
<ImageView
android:id="@+id/ivIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@mipmap/ic_launcher"
android:layout_marginRight="4dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/ivIcon"
android:textSize="14sp"
android:textColor="@color/app_dialog_title_color"
android:text="@string/app_updater_start_notification_content"/>
</RelativeLayout>

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:gravity="center_vertical"
android:background="@drawable/app_dialog_bg">
<ImageView
android:id="@+id/ivIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@mipmap/ic_launcher"
android:layout_marginRight="4dp"/>
<TextView
android:id="@+id/tvDialogTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/ivIcon"
android:textSize="14sp"
android:textColor="@color/app_dialog_title_color"
android:text="@string/app_updater_progress_notification_title"/>
<TextView
android:id="@+id/tvProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tvDialogTitle"
android:layout_marginTop="10dp"
android:text="@string/app_updater_start_notification_content"
android:textSize="14sp"
android:textColor="@color/app_dialog_content_color"
android:lineSpacingMultiplier="1.2" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="6dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:max="100"
android:layout_below="@+id/tvProgress"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>
</RelativeLayout>

@ -1,15 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#5588FF</color>
<color name="colorPrimaryDark">#4B7CFD</color>
<color name="colorAccent">#5588FF</color>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="ic_launcher_background">#FFFFFF</color>
<color name="white">#FFFFFF</color>
<color name="text_666">#666666</color>
<color name="text_999">#999999</color>
</resources>

@ -1,6 +1,4 @@
<resources>
<string name="app_name">AppUpdater</string>
<string name="ignore">忽略</string>
</resources>

@ -9,17 +9,6 @@
<item name="android:textAllCaps">false</item>
</style>
<style name="app_dialog_heads_up" parent="android:style/Theme.Dialog">
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowAnimationStyle">@style/app_dialog_top_animation</item>
</style>
<style name="OnClick">
<item name="android:clickable">true</item>
<item name="android:onClick">onClick</item>

@ -1,26 +1,25 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
apply from: 'versions.gradle'
repositories {
google()
mavenCentral()
jcenter()
}
addRepos(repositories)
dependencies {
classpath "com.android.tools.build:gradle:$versions.gradle"
classpath "com.vanniktech:gradle-maven-publish-plugin:$versions.mavenPublish"
classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'com.novoda:bintray-release:0.9'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.13.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}
addRepos(repositories)
}
task clean(type: Delete) {
@ -34,8 +33,8 @@ allprojects {
charSet 'UTF-8'
links "http://docs.oracle.com/javase/8/docs/api"
}
options.addStringOption('Xdoclint:none', '-quiet')
// options.compilerArgs << "-Xlint:unchecked" << "- Xlint:deprecation"
failOnError false
}
}

@ -11,12 +11,10 @@ org.gradle.jvmargs=-Xmx1536m
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true
VERSION_NAME=1.1.3
VERSION_CODE=22
VERSION_NAME=1.0.10
VERSION_CODE=17
GROUP=com.github.jenly1314.AppUpdater
POM_DESCRIPTION=AppUpdater for Android
@ -32,5 +30,4 @@ POM_DEVELOPER_ID=jenly
POM_DEVELOPER_NAME=Jenly Yu
RELEASE_REPOSITORY_URL=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
SNAPSHOT_REPOSITORY_URL=https://s01.oss.sonatype.org/content/repositories/snapshots/
RELEASE_SIGNING_ENABLED=false
RELEASE_SIGNING_ENABLED=true

@ -1,34 +1,56 @@
//App
def app_version = [:]
app_version.versionCode = 22
app_version.versionName = "1.1.3"
app_version.versionCode = 17 //androidx 18
app_version.versionName = "1.0.10"
ext.app_version = app_version
//build version
def build_versions = [:]
build_versions.minSdk = 15
build_versions.targetSdk = 29
build_versions.compileSdk = 29
build_versions.buildTools = "29.0.3"
build_versions.targetSdk = 28
build_versions.compileSdk = 28
build_versions.buildTools = "28.0.3"
ext.build_versions = build_versions
// App dependencies
def versions = [:]
//support
versions.supportLibrary = "28.0.0"
versions.constraintLayout = "1.1.3"
versions.mavenPublish = '0.13.0'
versions.gradle = "4.1.1"
//test
versions.junit = "4.12"
versions.runner = "1.0.2"
versions.espresso = "3.0.2"
versions.okhttp = "4.2.2"
ext.versions = versions
//androidx
versions.appcompat = "1.1.0"
versions.constraintLayout = "2.0.4"
ext.deps = [:]
//support
def support = [:]
support.appcompat = "com.android.support:appcompat-v7:$versions.supportLibrary"
support.constraintlayout = "com.android.support.constraint:constraint-layout:$versions.constraintLayout"
deps.support = support
//test
versions.junit = "1.2.0"
versions.junit = "4.13.1"
versions.androidExtJunit = "1.1.2"
versions.espressoCore = "3.2.0"
def test = [:]
test.junit = "junit:junit:$versions.junit"
test.runner = "com.android.support.test:runner:$versions.runner"
test.espresso = "com.android.support.test.espresso:espresso-core:$versions.espresso"
deps.test = test
versions.okhttp = "4.9.1"
//okHttp
deps.okhttp = "com.squareup.okhttp3:okhttp:$versions.okhttp"
ext.versions = versions
ext.deps = deps
def addRepos(RepositoryHandler handler) {
handler.google()
handler.mavenCentral()
handler.jcenter()
}
ext.addRepos = this.&addRepos
Loading…
Cancel
Save