Compare commits

..

41 Commits

Author SHA1 Message Date
Jenly 3dfb90809a 优化细节 3 years ago
Jenly 7f23dfb4ef 优化细节 3 years ago
Jenly 08e5c8fbb6 优化细节 3 years ago
Jenly ac88d07c1b 优化细节 3 years ago
Jenly 4ee839fe2c 优化细节 3 years ago
Jenly 1bf77be43a Update README 3 years ago
Jenly 5c848ee830 新增 ViewHolder 3 years ago
Jenly 5183b3b9fe 新增几种组合动画 3 years ago
Jenly 9a426841c9 对外提供更多可配置参数 3 years ago
Jenly 09972ded00 优化日志 3 years ago
Jenly 5139dfbfff Update README 3 years ago
Jenly 41a1f88d33 Update README 3 years ago
Jenly ff6eca1ab2 优化细节 3 years ago
Jenly e93a677d1b 优化细节 4 years ago
Jenly 3ee62c3b32 优化细节 4 years ago
Jenly 727cd08b98 优化细节 4 years ago
Jenly 898b3912b6 优化细节 4 years ago
Jenly 3a685c958f CI 4 years ago
Jenly 2677d67d70 CI 4 years ago
Jenly b5832c92da CI 4 years ago
Jenly 6d04cf4575 Update targetSdk to 29 4 years ago
Jenly 372b1ad30f maven publish 4 years ago
Jenly d2874564d7 优化细节 4 years ago
Jenly dbfbac55de * AppDialogConfig添加构造参数,简化自定义扩展用法 4 years ago
Jenly d3a759374c 优化细节 4 years ago
Jenly 5c1c0c1ad6 * 优化默认Dialog样式的显示细节 4 years ago
Jenly 2d56132692 优化细节 4 years ago
jenly1314 8e657d1ecb 支持MD5校验 5 years ago
jenly1314 636e202e2f 对外提供获取Dialog方法(#16) 5 years ago
jenly1314 e38db4de11 CI 5 years ago
Jenly 69a93148a2 CI 5 years ago
jenly1314 3838ad6685 优化细节 5 years ago
jenly1314 d29c1348ad 增加混淆 5 years ago
jenly1314 82a9b36a7d 优化细节 5 years ago
jenly1314 a10687ad07 优化细节 5 years ago
jenly1314 0a7e40dfce 优化细节 5 years ago
jenly1314 ea8f2603a8 CI 5 years ago
jenly1314 0a8de018b8 支持取消下载 5 years ago
jenly1314 4eee3649c9 支持AndroidX版本 5 years ago
jenly1314 ef8b918f1f 支持AndroidX版本 5 years ago
jenly1314 55e92e5f17 支持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. 9
      app-dialog/src/main/res/anim/app_dialog_left_in.xml
  14. 9
      app-dialog/src/main/res/anim/app_dialog_left_out.xml
  15. 9
      app-dialog/src/main/res/anim/app_dialog_right_in.xml
  16. 9
      app-dialog/src/main/res/anim/app_dialog_right_out.xml
  17. 16
      app-dialog/src/main/res/anim/app_dialog_scale_bottom_in.xml
  18. 16
      app-dialog/src/main/res/anim/app_dialog_scale_bottom_out.xml
  19. 5
      app-dialog/src/main/res/anim/app_dialog_scale_in.xml
  20. 16
      app-dialog/src/main/res/anim/app_dialog_scale_left_in.xml
  21. 16
      app-dialog/src/main/res/anim/app_dialog_scale_left_out.xml
  22. 5
      app-dialog/src/main/res/anim/app_dialog_scale_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. 43
      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. 261
      app-updater/src/main/java/com/king/app/updater/service/DownloadService.java
  47. 37
      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. 62
      app-updater/src/main/java/com/king/app/updater/util/NotificationUtils.java
  50. 39
      app-updater/src/main/java/com/king/app/updater/util/PermissionUtils.java
  51. 65
      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

@ -0,0 +1,20 @@
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 language: android
jdk: oraclejdk8 jdk: oraclejdk8
before_install: before_install:
- yes | sdkmanager "platforms;android-28" - yes | sdkmanager "platforms;android-29"
env: env:
global: global:
- ANDROID_API_LEVEL=28 - ANDROID_API_LEVEL=29
- ANDROID_BUILD_TOOLS_VERSION=28.0.3 - ANDROID_BUILD_TOOLS_VERSION=29.0.3
- TRAVIS_SECURE_ENV_VARS=true - TRAVIS_SECURE_ENV_VARS=true
android: android:

@ -3,6 +3,7 @@
![Image](app/src/main/ic_launcher-web.png) ![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) [![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) [![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) [![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) [![CI](https://travis-ci.org/jenly1314/AppUpdater.svg?branch=master)](https://travis-ci.org/jenly1314/AppUpdater)
@ -27,34 +28,47 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
- [x] 支持下载失败,重新下载 - [x] 支持下载失败,重新下载
- [x] 支持下载优先取本地缓存 - [x] 支持下载优先取本地缓存
- [x] 支持通知栏提示内容和过程全部可配置 - [x] 支持通知栏提示内容和过程全部可配置
- [x] 支持Android Q(10)
- [x] 支持取消下载 - [x] 支持取消下载
- [x] 支持使用OkHttpClient下载 - [x] 支持使用OkHttpClient下载
- [x] 支持Android 10(Q)
- [x] 支持Android 11(R)
- [x] 支持Android 12(S)
## Gif 展示 ## Gif 展示
![Image](GIF.gif) ![Image](GIF.gif)
> 你也可以直接下载 [演示App](https://raw.githubusercontent.com/jenly1314/AppUpdater/master/app/release/app-release.apk) 体验效果
## 引入 ## 引入
### Maven: ### Gradle:
```maven
//app-updater 1. 在Project的 **build.gradle** 里面添加远程仓库
<dependency>
<groupId>com.king.app</groupId> ```gradle
<artifactId>app-updater</artifactId> allprojects {
<version>1.0.10</version> repositories {
<type>pom</type> //...
</dependency> mavenCentral()
}
}
```
2. 在Module的 **build.gradle** 里面添加引入依赖项
```gradle
//----------AndroidX 版本
//app-updater
implementation 'com.github.jenly1314.AppUpdater:app-updater:1.1.3'
//app-dialog //app-dialog
<dependency> implementation 'com.github.jenly1314.AppUpdater:app-dialog:1.1.3'
<groupId>com.king.app</groupId>
<artifactId>app-dialog</artifactId>
<version>1.0.10</version>
<type>pom</type>
</dependency>
``` ```
### Gradle:
以前发布至JCenter的版本
```gradle ```gradle
//----------AndroidX 版本 //----------AndroidX 版本
@ -69,28 +83,6 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
//app-dialog //app-dialog
implementation 'com.king.app:app-dialog:1.0.10' 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' }
}
}
```
## 示例 ## 示例
@ -102,9 +94,9 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
//简单弹框升级 //简单弹框升级
AppDialogConfig config = new AppDialogConfig(context); AppDialogConfig config = new AppDialogConfig(context);
config.setTitle("简单弹框升级") config.setTitle("简单弹框升级")
.setOk("升级") .setConfirm("升级") //旧版本使用setOk
.setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、") .setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
.setOnClickOk(new View.OnClickListener() { .setOnClickConfirm(new View.OnClickListener() { //旧版本使用setOnClickOk
@Override @Override
public void onClick(View v) { public void onClick(View v) {
new AppUpdater.Builder() new AppUpdater.Builder()
@ -120,9 +112,9 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
//简单DialogFragment升级 //简单DialogFragment升级
AppDialogConfig config = new AppDialogConfig(context); AppDialogConfig config = new AppDialogConfig(context);
config.setTitle("简单DialogFragment升级") config.setTitle("简单DialogFragment升级")
.setOk("升级") .setConfirm("升级") //旧版本使用setOk
.setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、") .setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
.setOnClickOk(new View.OnClickListener() { .setOnClickConfirm(new View.OnClickListener() { //旧版本使用setOnClickOk
@Override @Override
public void onClick(View v) { public void onClick(View v) {
new AppUpdater.Builder() new AppUpdater.Builder()
@ -138,7 +130,7 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
``` ```
更多使用详情,请查看[app](app)中的源码使用示例或直接查看[API帮助文档](https://jenly1314.github.io/projects/AppUpdater/doc/) 更多使用详情,请查看[app](app)中的源码使用示例或直接查看[API帮助文档](https://javadoc.jitpack.io/com/github/jenly1314/AppUpdater/1.1.0/javadoc/index.html)
## 混淆 ## 混淆
@ -148,6 +140,22 @@ AppUpdater for Android 是一个专注于App更新,一键傻瓜式集成App版
## 版本记录 ## 版本记录
#### 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 #### v1.0.10:2021-3-4
* AppDialogConfig添加构造参数,简化自定义扩展用法 * AppDialogConfig添加构造参数,简化自定义扩展用法
* 优化细节 * 优化细节

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

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

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

@ -1,17 +1,36 @@
package com.king.app.dialog; package com.king.app.dialog;
import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.support.annotation.IdRes; import android.content.res.ColorStateList;
import android.support.annotation.LayoutRes; import android.graphics.Bitmap;
import android.support.annotation.NonNull; import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.util.Linkify;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.TypedValue;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.Button; 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 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;
/** /**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a> * App 对话框配置
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class AppDialogConfig extends BaseDialogConfig{ public class AppDialogConfig extends BaseDialogConfig{
@ -21,25 +40,39 @@ public class AppDialogConfig extends BaseDialogConfig{
private View view; private View view;
private ViewHolder viewHolder;
/**
* 构造
* @param context 上下文
*/
public AppDialogConfig(@NonNull Context context){ public AppDialogConfig(@NonNull Context context){
this(context,R.layout.app_dialog); this(context, R.layout.app_dialog);
} }
public AppDialogConfig(@NonNull Context context,@LayoutRes int layoutId){ /**
* 构造
* @param context 上下文
* @param layoutId 布局ID
*/
public AppDialogConfig(@NonNull Context context, @LayoutRes int layoutId){
super(layoutId); super(layoutId);
this.context = context; this.context = context;
views = new SparseArray<>(); views = new SparseArray<>();
} }
/**
* 获取上下文
* @return {@link #context}
*/
public Context getContext(){ public Context getContext(){
return context; return context;
} }
/** /**
* use {@link #getDialogView()} * 获取对话框视图
* @param context * @param context 上下文
* @return * @return 对话框视图
* @deprecated 即将废弃下一个版本可能会移除此方法 * @deprecated 即将废弃下一个版本可能会移除此方法
*/ */
@Deprecated @Deprecated
@ -47,17 +80,33 @@ public class AppDialogConfig extends BaseDialogConfig{
return getDialogView(); return getDialogView();
} }
public View getDialogView(){ /**
* 获取对话框视图
* @return 对话框视图
*/
private View getDialogView(){
if(view == null){ if(view == null){
view = LayoutInflater.from(context).inflate(getLayoutId(),null); view = LayoutInflater.from(context).inflate(getLayoutId(),null);
} }
return view; return view;
} }
/**
* 通过视图ID查找对应的视图
* @param id 视图ID
* @param <T> 对应的视图类
* @return 视图ID对应的视图
*/
private <T extends View> T findView(@IdRes int id){ private <T extends View> T findView(@IdRes int id){
return (T)getDialogView().findViewById(id); return getDialogView().findViewById(id);
} }
/**
* 根据视图ID获取对应的视图
* @param id 视图ID
* @param <T> 对应的视图类
* @return 视图ID对应的视图
*/
public <T extends View> T getView(@IdRes int id){ public <T extends View> T getView(@IdRes int id){
View v = views.get(id); View v = views.get(id);
if(v == null){ if(v == null){
@ -68,10 +117,9 @@ public class AppDialogConfig extends BaseDialogConfig{
return (T)v; return (T)v;
} }
/** /**
* 通过{@link AppDialogConfig} 创建一个视图 * 通过{@link AppDialogConfig} 创建一个视图
* @return * @return {@link View}
*/ */
View buildAppDialogView(){ View buildAppDialogView(){
TextView tvDialogTitle = getView(titleId); TextView tvDialogTitle = getView(titleId);
@ -97,21 +145,617 @@ public class AppDialogConfig extends BaseDialogConfig{
line.setVisibility(isHideCancel ? View.GONE : View.VISIBLE); line.setVisibility(isHideCancel ? View.GONE : View.VISIBLE);
} }
Button btnDialogOK = getView(okId); Button btnDialogConfirm = getView(confirmId);
if(btnDialogOK != null){ if(btnDialogConfirm != null){
setText(btnDialogOK,ok); setText(btnDialogConfirm,confirm);
btnDialogOK.setOnClickListener(onClickOk != null ? onClickOk : AppDialog.INSTANCE.mOnClickDismissDialog); btnDialogConfirm.setOnClickListener(onClickConfirm != null ? onClickConfirm : AppDialog.INSTANCE.mOnClickDismissDialog);
} }
return view; return view;
} }
private void setText(TextView tv,CharSequence text){
private void setText(TextView tv, CharSequence text){
if(text != null){ if(text != null){
tv.setText(text); 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,15 +1,19 @@
package com.king.app.dialog; package com.king.app.dialog;
import android.content.Context; import android.content.Context;
import android.support.annotation.IdRes; import android.view.Gravity;
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.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 Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a> * 基础对话框配置
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class BaseDialogConfig { public class BaseDialogConfig {
@ -32,7 +36,7 @@ public class BaseDialogConfig {
/** /**
* 确定视图ID右边按钮 * 确定视图ID右边按钮
*/ */
@IdRes int okId = R.id.btnDialogOK; @IdRes int confirmId = R.id.btnDialogConfirm;
/** /**
* 按钮中间分割线ID * 按钮中间分割线ID
*/ */
@ -43,6 +47,11 @@ public class BaseDialogConfig {
*/ */
@StyleRes int styleId = R.style.app_dialog; @StyleRes int styleId = R.style.app_dialog;
/**
* 对话框动画样式ID
*/
@StyleRes int animationStyleId = R.style.app_dialog_scale_animation;
/** /**
* 标题文本 * 标题文本
*/ */
@ -58,7 +67,7 @@ public class BaseDialogConfig {
/** /**
* 确定按钮文本 * 确定按钮文本
*/ */
CharSequence ok; CharSequence confirm;
/** /**
* 是否隐藏取消按钮如果隐藏取消则底部只显示一个按钮 * 是否隐藏取消按钮如果隐藏取消则底部只显示一个按钮
*/ */
@ -68,184 +77,593 @@ public class BaseDialogConfig {
*/ */
boolean isHideTitle; 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 onClickCancel;
View.OnClickListener onClickOk; View.OnClickListener onClickConfirm;
/**
* 构造
*/
public BaseDialogConfig(){ public BaseDialogConfig(){
this(R.layout.app_dialog); this(R.layout.app_dialog);
} }
/**
* 构造
* @param layoutId 布局ID
*/
public BaseDialogConfig(@LayoutRes int layoutId){ public BaseDialogConfig(@LayoutRes int layoutId){
this.layoutId = layoutId; this.layoutId = layoutId;
} }
/**
* 布局ID
* @return 布局ID
*/
public @LayoutRes int getLayoutId() { public @LayoutRes int getLayoutId() {
return layoutId; return layoutId;
} }
/** /**
* @param layoutId * 此方法即将废弃请通过构造{@link #BaseDialogConfig(int)}来初始化
* @return * @param layoutId 布局ID
* @return {@link BaseDialogConfig}
* @deprecated 即将废弃下一个版本可能会移除此方法 * @deprecated 即将废弃下一个版本可能会移除此方法
*/ */
@Deprecated @Deprecated
public BaseDialogConfig setLayoutId(@IdRes int layoutId) { public BaseDialogConfig setLayoutId(@LayoutRes int layoutId) {
this.layoutId = layoutId; this.layoutId = layoutId;
return this; return this;
} }
/**
* 标题视图ID
* @return 视图ID
*/
public int getTitleId() { public int getTitleId() {
return titleId; return titleId;
} }
/**
* 设置标题视图ID
* @param titleId 视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setTitleId(@IdRes int titleId) { public BaseDialogConfig setTitleId(@IdRes int titleId) {
this.titleId = titleId; this.titleId = titleId;
return this; return this;
} }
/**
* 样式ID
* @return 样式ID
*/
public int getStyleId() { public int getStyleId() {
return styleId; return styleId;
} }
public BaseDialogConfig setStyleId(@IdRes int styleId) { /**
* 设置Dialog样式ID(仅对Dialog有效如果使用的是DialogFragment请使用{@link #setAnimationStyleId(int)})
* @param styleId
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setStyleId(@StyleRes int styleId) {
this.styleId = styleId; this.styleId = styleId;
return this; 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() { public @IdRes int getContentId() {
return contentId; return contentId;
} }
/**
* 设置内容视图ID
* @param contentId 内容视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setContentId(@IdRes int contentId) { public BaseDialogConfig setContentId(@IdRes int contentId) {
this.contentId = contentId; this.contentId = contentId;
return this; return this;
} }
/**
* 取消按钮视图ID
* @return 视图ID
*/
public @IdRes int getCancelId() { public @IdRes int getCancelId() {
return cancelId; return cancelId;
} }
/**
* 设置取消按钮视图ID
* @param cancelId 取消按钮视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancelId(@IdRes int cancelId) { public BaseDialogConfig setCancelId(@IdRes int cancelId) {
this.cancelId = cancelId; this.cancelId = cancelId;
return this; return this;
} }
/**
* 获取确定按钮视图ID
* @return 确定按钮视图ID
* @Deprecated 请使用 {@link #getConfirmId()}来代替后续版本可能会移除此方法
*/
@Deprecated
public @IdRes int getOkId() { public @IdRes int getOkId() {
return okId; return getConfirmId();
} }
/**
* 设置确定按钮视图ID
* @param okId 确定按钮视图ID
* @return {@link BaseDialogConfig}
* @Deprecated 请使用 {@link #setConfirmId(int)}来代替后续版本可能移除此方法
*/
@Deprecated
public BaseDialogConfig setOkId(@IdRes int okId) { public BaseDialogConfig setOkId(@IdRes int okId) {
this.okId = 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;
return this; return this;
} }
/**
* 分割线视图ID
* @return 视图ID
*/
public @IdRes int getLineId() { public @IdRes int getLineId() {
return lineId; return lineId;
} }
/**
* 设置分割线视图ID
* @param lineId 分割线视图ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setLineId(@IdRes int lineId) { public BaseDialogConfig setLineId(@IdRes int lineId) {
this.lineId = lineId; this.lineId = lineId;
return this; return this;
} }
/**
* 标题
* @return 标题
*/
public CharSequence getTitle() { public CharSequence getTitle() {
return title; return title;
} }
/**
* 设置标题
* @param title 标题
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setTitle(CharSequence title) { public BaseDialogConfig setTitle(CharSequence title) {
this.title = title; this.title = title;
return this; return this;
} }
/**
* 设置标题
* @param context 上下文
* @param resId 标题资源ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setTitle(@NonNull Context context, @StringRes int resId) { public BaseDialogConfig setTitle(@NonNull Context context, @StringRes int resId) {
this.title = context.getString(resId); this.title = context.getString(resId);
return this; return this;
} }
/**
* 文本内容
* @return 文本内容
*/
public CharSequence getContent() { public CharSequence getContent() {
return content; return content;
} }
/**
* 设置文本内容
* @param content 文本内容
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setContent(CharSequence content) { public BaseDialogConfig setContent(CharSequence content) {
this.content = content; this.content = content;
return this; return this;
} }
/**
* 取消按钮文本内容
* @return 取消按钮文本内容
*/
public CharSequence getCancel() { public CharSequence getCancel() {
return cancel; return cancel;
} }
/**
* 设置取消按钮文本内容
* @param cancel 取消按钮文本内容
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancel(CharSequence cancel) { public BaseDialogConfig setCancel(CharSequence cancel) {
this.cancel = cancel; this.cancel = cancel;
return this; return this;
} }
/**
* 设置取消按钮文本内容
* @param context 上下文
* @param resId 取消按钮文本内容资源ID
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setCancel(@NonNull Context context, @StringRes int resId) { public BaseDialogConfig setCancel(@NonNull Context context, @StringRes int resId) {
this.cancel = context.getString(resId); this.cancel = context.getString(resId);
return this; return this;
} }
/**
* 获取确定按钮文本内容
* @return 确定按钮文本内容
* @deprecated 请使用 {@link #getConfirm()} 来代替后续版本可能会移除此方法
*/
public CharSequence getOk() { public CharSequence getOk() {
return ok; return getConfirm();
} }
/**
* 设置确定按钮文本内容
* @param ok 确定按钮文本内容
* @return {@link BaseDialogConfig}
* @deprecated 请使用 {@link #setConfirm(CharSequence)} 来代替后续版本可能会移除此方法
*/
public BaseDialogConfig setOk(CharSequence ok) { public BaseDialogConfig setOk(CharSequence ok) {
this.ok = ok; return setConfirm(ok);
return this;
} }
/**
* 设置确定按钮文本内容
* @param context 上下文
* @param resId 确定按钮文本内容资源ID
* @return {@link BaseDialogConfig}
* @deprecated 请使用 {@link #setConfirm(Context, int)}来代替后续版本可能会移除此方法
*/
public BaseDialogConfig setOk(@NonNull Context context, @StringRes int resId) { public BaseDialogConfig setOk(@NonNull Context context, @StringRes int resId) {
this.ok = context.getString(resId); return setConfirm(context,resId);
}
/**
* 确定按钮文本内容
* @return 确定按钮文本内容
*/
public CharSequence getConfirm() {
return confirm;
}
/**
* 设置确定按钮文本内容
* @param confirm 确定按钮文本内容
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setConfirm(CharSequence confirm) {
this.confirm = confirm;
return this; return this;
} }
/**
* 设置确定按钮文本内容
* @param context 上下文
* @param resId 确定按钮文本内容资源ID
* @return {@link BaseDialogConfig}
*
*/
public BaseDialogConfig setConfirm(@NonNull Context context, @StringRes int resId) {
this.confirm = context.getString(resId);
return this;
}
/**
* 是否隐藏取消按钮
* @return {@link #isHideCancel}
*/
public boolean isHideCancel() { public boolean isHideCancel() {
return isHideCancel; return isHideCancel;
} }
/**
* 设置是否隐藏取消按钮
* @param hideCancel 是否隐藏取消按钮
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setHideCancel(boolean hideCancel) { public BaseDialogConfig setHideCancel(boolean hideCancel) {
isHideCancel = hideCancel; isHideCancel = hideCancel;
return this; return this;
} }
/**
* 是否隐藏标题
* @return {@link #isHideTitle}
*/
public boolean isHideTitle(){ public boolean isHideTitle(){
return isHideTitle; return isHideTitle;
} }
/**
* 设置是否隐藏标题
* @param hideTitle 是否隐藏标题
* @return {@link BaseDialogConfig}
*/
public BaseDialogConfig setHideTitle(boolean hideTitle){ public BaseDialogConfig setHideTitle(boolean hideTitle){
isHideTitle = hideTitle; isHideTitle = hideTitle;
return this; 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() { public View.OnClickListener getOnClickCancel() {
return onClickCancel; return onClickCancel;
} }
/** /**
* 设置取消按钮点击监听不设置默认点击关闭弹框 * 设置取消按钮点击监听不设置默认点击关闭弹框
* @param onClickCancel * @param onClickCancel 取消按钮点击监听
* @return * @return {@link BaseDialogConfig}
*/ */
public BaseDialogConfig setOnClickCancel(View.OnClickListener onClickCancel) { public BaseDialogConfig setOnClickCancel(View.OnClickListener onClickCancel) {
this.onClickCancel = onClickCancel; this.onClickCancel = onClickCancel;
return this; 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() { public View.OnClickListener getOnClickOk() {
return onClickOk; return getOnClickConfirm();
} }
/** /**
* 设置确定按钮点击监听不设置默认点击关闭弹框 * 设置确定按钮点击监听不设置默认点击关闭弹框
* @param onClickOk * @param onClickOk 确定按钮点击监听
* @return * @return {@link BaseDialogConfig}
* @deprecated 请使用 {@link #setOnClickConfirm(View.OnClickListener)}来代替后续版本可能会移除此方法
*/ */
public BaseDialogConfig setOnClickOk(View.OnClickListener onClickOk) { public BaseDialogConfig setOnClickOk(View.OnClickListener onClickOk) {
this.onClickOk = 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;
return this; 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,5 +1,7 @@
package com.king.app.dialog.fragment; package com.king.app.dialog.fragment;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
@ -9,6 +11,7 @@ import com.king.app.dialog.BaseDialogConfig;
import com.king.app.dialog.R; import com.king.app.dialog.R;
/** /**
* App对话框 Fragment封装便捷的对话框API使用时更简单
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public class AppDialogFragment extends BaseDialogFragment { public class AppDialogFragment extends BaseDialogFragment {
@ -16,7 +19,6 @@ public class AppDialogFragment extends BaseDialogFragment {
private BaseDialogConfig config; private BaseDialogConfig config;
public static AppDialogFragment newInstance(BaseDialogConfig config) { public static AppDialogFragment newInstance(BaseDialogConfig config) {
Bundle args = new Bundle(); Bundle args = new Bundle();
AppDialogFragment fragment = new AppDialogFragment(); AppDialogFragment fragment = new AppDialogFragment();
fragment.config = config; fragment.config = config;
@ -57,12 +59,22 @@ public class AppDialogFragment extends BaseDialogFragment {
line.setVisibility(config.isHideCancel() ? View.GONE : View.VISIBLE); line.setVisibility(config.isHideCancel() ? View.GONE : View.VISIBLE);
} }
Button btnDialogOK = rootView.findViewById(config.getOkId()); Button btnDialogConfirm = rootView.findViewById(config.getConfirmId());
if(btnDialogOK != null){ if(btnDialogConfirm != null){
setText(btnDialogOK,config.getOk()); setText(btnDialogConfirm,config.getConfirm());
btnDialogOK.setOnClickListener(config.getOnClickOk() != null ? config.getOnClickOk() : getOnClickDismiss()); btnDialogConfirm.setOnClickListener(config.getOnClickConfirm() != null ? config.getOnClickConfirm() : 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,9 +5,7 @@ import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.view.Gravity;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -17,7 +15,12 @@ import android.widget.TextView;
import com.king.app.dialog.R; 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> * @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public abstract class BaseDialogFragment extends DialogFragment { public abstract class BaseDialogFragment extends DialogFragment {
@ -26,32 +29,78 @@ public abstract class BaseDialogFragment extends DialogFragment {
private View mRootView; private View mRootView;
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 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); mRootView = inflater.inflate(getRootLayoutId(), container, false);
init(mRootView); init(mRootView);
return mRootView; return mRootView;
} }
@NonNull
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); Dialog dialog = super.onCreateDialog(savedInstanceState);
super.getDialog().getWindow().getAttributes().windowAnimations = R.style.app_dialog_animation; dialog.setCanceledOnTouchOutside(false);
getDialog().setCanceledOnTouchOutside(false); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
setDialogWindow(getContext(),getDialog(),DEFAULT_WIDTH_RATIO); initDialogWindow(getContext(),dialog,Gravity.NO_GRAVITY,DEFAULT_WIDTH_RATIO,0, 0, 0, 0, 0, 0, R.style.app_dialog_scale_animation);
return dialog;
} }
private void setDialogWindow(Context context, Dialog dialog, float widthRatio){ /**
* 初始化对话框视图
* @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);
}
/**
* 设置弹框窗口配置
* @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){
Window window = dialog.getWindow(); Window window = dialog.getWindow();
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
WindowManager.LayoutParams lp = window.getAttributes(); WindowManager.LayoutParams lp = window.getAttributes();
lp.windowAnimations = animationStyleId;
lp.width = (int)(context.getResources().getDisplayMetrics().widthPixels * widthRatio); 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); window.setAttributes(lp);
} }
/**
* 根视图
* @return
*/
protected View getRootView(){ protected View getRootView(){
return mRootView; return mRootView;
} }
@ -73,8 +122,16 @@ public abstract class BaseDialogFragment extends DialogFragment {
} }
}; };
/**
* 获取根布局ID
* @return 根布局ID
*/
public abstract int getRootLayoutId(); public abstract int getRootLayoutId();
/**
* 初始化
* @param rootView
*/
public abstract void init(View rootView); public abstract void init(View rootView);
} }

@ -0,0 +1,9 @@
<?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>

@ -0,0 +1,9 @@
<?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>

@ -0,0 +1,9 @@
<?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>

@ -0,0 +1,9 @@
<?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>

@ -0,0 +1,9 @@
<?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>

@ -0,0 +1,9 @@
<?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>

@ -0,0 +1,16 @@
<?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>

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

@ -0,0 +1,16 @@
<?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>

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

@ -0,0 +1,16 @@
<?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>

@ -0,0 +1,16 @@
<?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>

@ -0,0 +1,16 @@
<?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>

@ -0,0 +1,16 @@
<?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>

@ -0,0 +1,9 @@
<?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>

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

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="colorAccent">#FF4081</color> <color name="colorAccent">#5588FF</color>
<color name="app_dialog_title_color">#333333</color> <color name="app_dialog_title_color">#333333</color>
<color name="app_dialog_content_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_normal_color">#333333</color>
<color name="app_dialog_button_pressed_color">@color/colorAccent</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> </resources>

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

@ -2,9 +2,52 @@
<resources> <resources>
<!-- dialog animation --> <!-- dialog animation -->
<style name="app_dialog_animation" parent="android:style/Animation.Dialog"> <style name="app_dialog_scale_animation" parent="android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/app_dialog_in</item> <item name="android:windowEnterAnimation">@anim/app_dialog_scale_in</item>
<item name="android:windowExitAnimation">@anim/app_dialog_out</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> </style>
<!-- dialog style --> <!-- dialog style -->
@ -15,7 +58,7 @@
<item name="android:windowNoTitle">true</item> <item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item> <item name="android:backgroundDimEnabled">true</item>
<item name="android:windowAnimationStyle">@style/app_dialog_animation</item> <item name="android:windowAnimationStyle">@style/app_dialog_scale_animation</item>
</style> </style>
</resources> </resources>

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

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

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

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

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

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

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

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

@ -1,13 +1,14 @@
package com.king.app.updater.http; package com.king.app.updater.http;
import android.support.annotation.Nullable;
import java.io.File; import java.io.File;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; 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> * @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public interface IHttpManager { public interface IHttpManager {

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

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

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

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

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

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

@ -0,0 +1,316 @@
/*
Copyright © 2015, 2016 Jenly Yu <a href="mailto:jenly1314@gmail.com">Jenly</a>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.king.app.updater.util;
import android.util.Log;
import java.util.Locale;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class LogUtils {
public static final String TAG = "AppUpdater";
public static final String VERTICAL = "|";
/** 是否显示Log日志 */
private static boolean isShowLog = true;
/** Log日志优先权 */
private static int priority = 1;
/**
* Priority constant for the println method;use System.out.println
*/
public static final int PRINTLN = 1;
/**
* Priority constant for the println method; use Log.v.
*/
public static final int VERBOSE = 2;
/**
* Priority constant for the println method; use Log.d.
*/
public static final int DEBUG = 3;
/**
* Priority constant for the println method; use Log.i.
*/
public static final int INFO = 4;
/**
* Priority constant for the println method; use Log.w.
*/
public static final int WARN = 5;
/**
* Priority constant for the println method; use Log.e.
*/
public static final int ERROR = 6;
/**
* Priority constant for the println method.use Log.wtf.
*/
public static final int ASSERT = 7;
public static final String TAG_FORMAT = "%s.%s(%s:%d)";
private LogUtils(){
throw new AssertionError();
}
public static void setShowLog(boolean isShowLog) {
LogUtils.isShowLog = isShowLog;
}
public static boolean isShowLog() {
return isShowLog;
}
public static int getPriority() {
return priority;
}
public static void setPriority(int priority) {
LogUtils.priority = priority;
}
/**
* 根据堆栈生成TAG
* @return TAG|className.methodName(fileName:lineNumber)
*/
private static String generateTag(StackTraceElement caller) {
String tag = TAG_FORMAT;
String callerClazzName = caller.getClassName();
callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
tag = String.format(Locale.getDefault(),tag,callerClazzName, caller.getMethodName(),caller.getFileName(),caller.getLineNumber());
return new StringBuilder().append(TAG).append(VERTICAL).append(tag).toString();
}
/**
* 获取堆栈
* @param n
* n=0 VMStack
* n=1 Thread
* n=3 CurrentStack
* n=4 CallerStack
* ...
* @return
*/
public static StackTraceElement getStackTraceElement(int n) {
return Thread.currentThread().getStackTrace()[n];
}
/**
* 获取调用方的堆栈TAG
* @return
*/
private static String getCallerStackLogTag(){
return generateTag(getStackTraceElement(5));
}
/**
*
* @param t
* @return
*/
private static String getStackTraceString(Throwable t){
return Log.getStackTraceString(t);
}
// -----------------------------------Log.v
/**
* Log.v
* @param msg
*/
public static void v(String msg) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), String.valueOf(msg));
}
public static void v(Throwable t) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), getStackTraceString(t));
}
public static void v(String msg,Throwable t) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.d
/**
* Log.d
* @param msg
*/
public static void d(String msg) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), String.valueOf(msg));
}
public static void d(Throwable t) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), getStackTraceString(t));
}
public static void d(String msg,Throwable t) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.i
/**
* Log.i
* @param msg
*/
public static void i(String msg) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), String.valueOf(msg));
}
public static void i(Throwable t) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), getStackTraceString(t));
}
public static void i(String msg,Throwable t) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.w
/**
* Log.w
* @param msg
*/
public static void w(String msg) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), String.valueOf(msg));
}
public static void w(Throwable t) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), getStackTraceString(t));
}
public static void w(String msg,Throwable t) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.e
/**
* Log.e
* @param msg
*/
public static void e(String msg) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), String.valueOf(msg));
}
public static void e(Throwable t) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), getStackTraceString(t));
}
public static void e(String msg,Throwable t) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.wtf
/**
* Log.wtf
* @param msg
*/
public static void wtf(String msg) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), String.valueOf(msg));
}
public static void wtf(Throwable t) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), getStackTraceString(t));
}
public static void wtf(String msg,Throwable t) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------System.out.print
/**
* System.out.print
*
* @param msg
*/
public static void print(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.print(msg);
}
public static void print(Object obj) {
if (isShowLog && priority <= PRINTLN)
System.out.print(obj);
}
// -----------------------------------System.out.printf
/**
* System.out.printf
*
* @param msg
*/
public static void printf(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.printf(msg);
}
// -----------------------------------System.out.println
/**
* System.out.println
*
* @param msg
*/
public static void println(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.println(msg);
}
public static void println(Object obj) {
if (isShowLog && priority <= PRINTLN)
System.out.println(obj);
}
}

@ -7,9 +7,6 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; 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.UpdateConfig;
import com.king.app.updater.constant.Constants; import com.king.app.updater.constant.Constants;
@ -17,6 +14,10 @@ import com.king.app.updater.service.DownloadService;
import java.io.File; 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> * @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
@ -27,7 +28,7 @@ public class NotificationUtils {
} }
/** /**
* 显示开始下载的通知 * 显示开始下载的通知
* @param notifyId * @param notifyId
* @param channelId * @param channelId
* @param channelName * @param channelName
@ -35,12 +36,12 @@ public class NotificationUtils {
* @param title * @param title
* @param content * @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){ 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){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createNotificationChannel(context,channelId,channelName,isVibrate,isSound); createNotificationChannel(context,channelId,channelName,isVibrate,isSound);
} }
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content); NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setPriority(NotificationManager.IMPORTANCE_HIGH); builder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if(isVibrate && isSound){ if(isVibrate && isSound){
builder.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND); builder.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND);
}else if(isVibrate){ }else if(isVibrate){
@ -49,8 +50,20 @@ public class NotificationUtils {
builder.setDefaults(Notification.DEFAULT_SOUND); 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(); 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); notifyNotification(context,notifyId,notification);
} }
@ -64,11 +77,24 @@ public class NotificationUtils {
* @param progress * @param progress
* @param size * @param size
*/ */
public static void showProgressNotification(Context context, int notifyId,String channelId,@DrawableRes int icon,CharSequence title,CharSequence content,int progress,int size){ public static void showProgressNotification(Context context, int notifyId,String channelId,@DrawableRes int icon,CharSequence title,CharSequence content,int progress,int size, boolean isCancelDownload){
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content,progress,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(); 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); notifyNotification(context,notifyId,notification);
} }
@ -86,7 +112,7 @@ public class NotificationUtils {
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content); NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setAutoCancel(true); builder.setAutoCancel(true);
Intent intent = AppUtils.getInstallIntent(context,file,authority); Intent intent = AppUtils.getInstallIntent(context,file,authority);
PendingIntent clickIntent = PendingIntent.getActivity(context, notifyId,intent, PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent clickIntent = PendingIntent.getActivity(context, notifyId, intent, getPendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT));
builder.setContentIntent(clickIntent); builder.setContentIntent(clickIntent);
Notification notification = builder.build(); Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL; notification.flags = Notification.FLAG_AUTO_CANCEL;
@ -107,14 +133,15 @@ public class NotificationUtils {
public static void showErrorNotification(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, boolean isReDownload, UpdateConfig config){ 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); NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setAutoCancel(true); builder.setAutoCancel(true);
if(isReDownload){//重新下载 int flag = getPendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT);
if(isReDownload){// 重新下载
Intent intent = new Intent(context, DownloadService.class); Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_RE_DOWNLOAD,true); intent.putExtra(Constants.KEY_RE_DOWNLOAD,true);
intent.putExtra(Constants.KEY_UPDATE_CONFIG,config); intent.putExtra(Constants.KEY_UPDATE_CONFIG,config);
PendingIntent clickIntent = PendingIntent.getService(context, notifyId,intent, PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent clickIntent = PendingIntent.getService(context, notifyId,intent, flag);
builder.setContentIntent(clickIntent); builder.setContentIntent(clickIntent);
}else{ }else{
PendingIntent clickIntent = PendingIntent.getService(context, notifyId,new Intent(), PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent clickIntent = PendingIntent.getService(context, notifyId, new Intent(), flag);
builder.setContentIntent(clickIntent); builder.setContentIntent(clickIntent);
} }
@ -203,7 +230,7 @@ public class NotificationUtils {
builder.setContentText(content); builder.setContentText(content);
builder.setOngoing(true); builder.setOngoing(true);
if(progress!= Constants.NONE && size!=Constants.NONE){ if(progress != Constants.NONE && size != Constants.NONE){
builder.setProgress(size,progress,false); builder.setProgress(size,progress,false);
} }
@ -219,4 +246,15 @@ public class NotificationUtils {
getNotificationManager(context).notify(id,notification); 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,62 +8,61 @@ import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
/** /**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a> * @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/ */
public final class PermissionUtils { public final class PermissionUtils {
private static String[] PERMISSIONS_STORAGE = { private PermissionUtils() {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE};
private PermissionUtils(){
throw new AssertionError(); throw new AssertionError();
} }
/** /**
* 校验权限 * 校验权限
*
* @param activity * @param activity
* @param requestCode * @param requestCode
* @return * @return
*/ */
public static boolean verifyReadAndWritePermissions(@NonNull Activity activity,int requestCode){ public static boolean verifyReadAndWritePermissions(@NonNull Activity activity, int requestCode) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
int readResult = checkPermission(activity,Manifest.permission.READ_EXTERNAL_STORAGE); int readResult = checkPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
int writeResult = checkPermission(activity,Manifest.permission.WRITE_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) {
if(readResult != PackageManager.PERMISSION_GRANTED || writeResult != PackageManager.PERMISSION_GRANTED || readPhoneState != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(activity, new String[]{
ActivityCompat.requestPermissions(activity,PERMISSIONS_STORAGE,requestCode); Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
return false; return false;
} }
}
return true; return true;
} }
public static int checkPermission(@NonNull Activity activity,@NonNull String permission){ public static int checkPermission(@NonNull Activity activity, @NonNull String permission) {
return ActivityCompat.checkSelfPermission(activity,permission); return ActivityCompat.checkSelfPermission(activity, permission);
} }
/** /**
* 获取通知权限 * 获取通知权限
*
* @param context * @param context
*/ */
public static boolean isNotificationEnabled(Context context) { public static boolean isNotificationEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 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) { if (notificationManager.getImportance() == NotificationManager.IMPORTANCE_NONE) {
return false; 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 CHECK_OP_NO_THROW = "checkOpNoThrow";
String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION"; String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";

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

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

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

Binary file not shown.

@ -0,0 +1,18 @@
{
"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"
}
]
}

@ -1 +0,0 @@
[{"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,12 +1,13 @@
package com.king.appupdater; package com.king.appupdater;
import android.content.Context; import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**

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

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

@ -0,0 +1,5 @@
<?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>

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

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

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

@ -0,0 +1,25 @@
<?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>

@ -0,0 +1,43 @@
<?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,10 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="colorPrimary">#3F51B5</color> <color name="colorPrimary">#5588FF</color>
<color name="colorPrimaryDark">#303F9F</color> <color name="colorPrimaryDark">#4B7CFD</color>
<color name="colorAccent">#FF4081</color> <color name="colorAccent">#5588FF</color>
<color name="ic_launcher_background">#FFFFFF</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> </resources>

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

@ -9,6 +9,17 @@
<item name="android:textAllCaps">false</item> <item name="android:textAllCaps">false</item>
</style> </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"> <style name="OnClick">
<item name="android:clickable">true</item> <item name="android:clickable">true</item>
<item name="android:onClick">onClick</item> <item name="android:onClick">onClick</item>

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

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

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