Repository: Ray512512/FastUpdate
Branch: master
Commit: cfc5c9ffaa1d
Files: 48
Total size: 54.4 KB
Directory structure:
gitextract_b0p0yxwt/
├── .gitignore
├── .idea/
│ ├── gradle.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── runConfigurations.xml
│ └── vcs.xml
├── README.md
├── UpdateAppUtils/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── teprinciple/
│ │ └── library/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ ├── customview/
│ │ │ │ └── ConfirmDialog.java
│ │ │ ├── feature/
│ │ │ │ └── Callback.java
│ │ │ └── util/
│ │ │ ├── DownloadAppUtils.java
│ │ │ ├── UpdateAppReceiver.java
│ │ │ └── UpdateAppUtils.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── bg_dialog_confirm.xml
│ │ ├── layout/
│ │ │ └── dialog_confirm.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── teprinciple/
│ └── library/
│ └── ExampleUnitTest.java
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── example/
│ │ └── teprinciple/
│ │ └── updateappdemo/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── teprinciple/
│ │ │ └── updateappdemo/
│ │ │ ├── KotlinDemoActivity.kt
│ │ │ └── MainActivity.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── bg_dialog_confirm.xml
│ │ ├── layout/
│ │ │ ├── activity_kotlin_demo.xml
│ │ │ ├── activity_main.xml
│ │ │ └── activity_start.xml
│ │ ├── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── values-w820dp/
│ │ │ └── dimens.xml
│ │ └── xml/
│ │ └── file_paths.xml
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ └── teprinciple/
│ └── updateappdemo/
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
================================================
FILE: .idea/gradle.xml
================================================
================================================
FILE: .idea/misc.xml
================================================
================================================
FILE: .idea/modules.xml
================================================
================================================
FILE: .idea/runConfigurations.xml
================================================
================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: README.md
================================================
# UpdateAppUtils1.4
### 一行代码,快速实现app在线下载更新
A simple library for Android update app

## 集成
compile引入
```
dependencies {
compile 'com.teprinciple:updateapputils:1.4'
}
```
## 使用
更新检测一般放在MainActivity或者启动页上,
在请求服务器版本检测接口获取到versionCode、versionName、最新apkPath后调用。
#### 快速使用
```
UpdateAppUtils.from(this)
.serverVersionCode(2) //服务器versionCode
.serverVersionName("2.0") //服务器versionName
.apkPath(apkPath) //最新apk下载地址
.update();
```
#### Kotlin代码调用完全一样
```
private fun update() {
val apkPath:String = "http://issuecdn.baidupcs.com/issue/netdisk/apk/BaiduNetdisk_7.15.1.apk"
UpdateAppUtils.from(this)
.serverVersionCode(2)
.serverVersionName("2.0")
.apkPath(apkPath)
.update()
}
```
#### 更多配置使用
```
UpdateAppUtils.from(this)
.checkBy(UpdateAppUtils.CHECK_BY_VERSION_NAME) //更新检测方式,默认为VersionCode
.serverVersionCode(2)
.serverVersionName("2.0")
.apkPath(apkPath)
.showNotification(false) //是否显示下载进度到通知栏,默认为true
.updateInfo(info) //更新日志信息 String
.downloadBy(UpdateAppUtils.DOWNLOAD_BY_BROWSER) //下载方式:app下载、手机浏览器下载。默认app下载
.isForce(true) //是否强制更新,默认false 强制更新情况下用户不同意更新则不能使用app
.update();
```
#### 说明
```
1、UpdateAppUtils提供两种更新判断方式
CHECK_BY_VERSION_CODE:通过versionCode判断,服务器上versionCode > 本地versionCode则执行更新
CHECK_BY_VERSION_NAME:通过versionName判断,服务器上versionName 与 本地versionName不同则更新
2、UpdateAppUtils提供两种下载apk方式
DOWNLOAD_BY_APP:通过App下载
DOWNLOAD_BY_BROWSER:通过手机浏览器下载
```
#### 关于适配Android6.0、Android7.0
##### 适配Android7.0
1、注册provider
```
/provider>
```
2、新建file_paths.xml文件
```
```
可参见demo中的代码
如果你的版本没有适配到Android7.0 为了不进行上述操作,可以直接这样设置:
```
UpdateAppUtils.needFitAndroidN(false)
```
##### 适配Android6.0
关于6.0适配,请自行在调用API时申请WRITE_EXTERNAL_STORAGE权限,可以参考demo中的代码
#### 更新日志
1.4
使用[filedownloader](https://github.com/lingochamp/FileDownloader)替换DownloadManager,避免部分手机DownLoadManager无效,同时解决了重复下载的问题,且提高了下载速度
增加接口UpdateAppUtils.needFitAndroidN(false),避免不需要适配7.0,也要设置FileProvider
1.3.1
修复部分bug,在demo中加入kotlin调用代码
1.3
增加接口方法 showNotification(false)//是否显示下载进度到通知栏;
updateInfo(info)//更新日志信息;下载前WiFi判断。
1.2
适配Android7.0,并在demo中加入适配6.0和7.0的代码
1.1
适配更多SdkVersion
================================================
FILE: UpdateAppUtils/.gitignore
================================================
/build
================================================
FILE: UpdateAppUtils/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
minSdkVersion 14
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
compile 'com.liulishuo.filedownloader:library:1.6.8'
}
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
// This is the library version used when deploying the artifact
version = "1.4"
def siteUrl = 'https://github.com/teprinciple/UpdateAppDemo' // 项目的主页
def gitUrl = 'https://github.com/teprinciple/UpdateAppDemo.git' // Git仓库的url
group = "com.teprinciple" // Maven Group ID for the artifact,一般填你唯一的包名
install {
repositories.mavenInstaller {
// This generates POM.file_paths with proper parameters
pom {
project {
packaging 'aar'
// Add your description here
name 'A Simple library for Android update app'
url siteUrl
// Set your license
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id 'teprinciple' //填写的一些基本信息
name 'teprinciple'
email 'teprinciple@foxmail.com'
}
}
scm {
connection gitUrl
developerConnection gitUrl
url siteUrl
}
}
}
}
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
bintray {
user = properties.getProperty("bintray.user")
key = properties.getProperty("bintray.apikey")
configurations = ['archives']
pkg {
repo = "maven" ////跟上面创建的Maven仓库名字保持一致
name = "UpdateAppUtils" //发布到JCenter上的项目名字
websiteUrl = siteUrl
vcsUrl = gitUrl
licenses = ["Apache-2.0"]
publish = true
}
}
javadoc { //jav doc采用utf-8编码否则会报“GBK的不可映射字符”错误
options{
encoding "UTF-8"
charSet 'UTF-8'
}
}
================================================
FILE: UpdateAppUtils/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/teprinciple/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: UpdateAppUtils/src/androidTest/java/teprinciple/library/ExampleInstrumentedTest.java
================================================
package teprinciple.library;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("teprinciple.library.test", appContext.getPackageName());
}
}
================================================
FILE: UpdateAppUtils/src/main/AndroidManifest.xml
================================================
================================================
FILE: UpdateAppUtils/src/main/java/customview/ConfirmDialog.java
================================================
package customview;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import feature.Callback;
import teprinciple.updateapputils.R;
/**
* Created by Teprinciple on 2016/10/13.
*/
public class ConfirmDialog extends Dialog {
Callback callback;
private TextView content;
private TextView sureBtn;
private TextView cancleBtn;
public ConfirmDialog(Context context, Callback callback) {
super(context, R.style.CustomDialog);
this.callback = callback;
setCustomDialog();
}
private void setCustomDialog() {
View mView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_confirm, null);
sureBtn = (TextView)mView.findViewById(R.id.dialog_confirm_sure);
cancleBtn = (TextView)mView.findViewById(R.id.dialog_confirm_cancle);
content = (TextView) mView.findViewById(R.id.dialog_confirm_title);
sureBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callback.callback(1);
ConfirmDialog.this.cancel();
}
});
cancleBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callback.callback(0);
ConfirmDialog.this.cancel();
}
});
super.setContentView(mView);
}
public ConfirmDialog setContent(String s){
content.setText(s);
return this;
}
}
================================================
FILE: UpdateAppUtils/src/main/java/feature/Callback.java
================================================
package feature;
/**
* Created by sanmu on 2016/10/13 0013.
*/
public interface Callback {
public void callback(int position);
}
================================================
FILE: UpdateAppUtils/src/main/java/util/DownloadAppUtils.java
================================================
package util;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.liulishuo.filedownloader.BaseDownloadTask;
import com.liulishuo.filedownloader.FileDownloadLargeFileListener;
import com.liulishuo.filedownloader.FileDownloader;
import java.io.File;
/**
* Created by Teprinciple on 2016/12/13.
*/
class DownloadAppUtils {
private static final String TAG = DownloadAppUtils.class.getSimpleName();
public static long downloadUpdateApkId = -1;//下载更新Apk 下载任务对应的Id
public static String downloadUpdateApkFilePath;//下载更新Apk 文件路径
/**
* 通过浏览器下载APK包
* @param context
* @param url
*/
public static void downloadForWebView(Context context, String url) {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
public static void download(final Context context, String url,final String serverVersionName) {
String packageName = context.getPackageName();
String filePath = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {//外部存储卡
filePath = Environment.getExternalStorageDirectory().getAbsolutePath();
} else {
Log.i(TAG, "没有SD卡");
return;
}
String apkLocalPath= filePath + File.separator + packageName + "_"+serverVersionName+".apk";
downloadUpdateApkFilePath = apkLocalPath;
FileDownloader.setup(context);
FileDownloader.getImpl().create(url)
.setPath(apkLocalPath)
.setListener(new FileDownloadLargeFileListener() {
@Override
protected void pending(BaseDownloadTask task, long soFarBytes, long totalBytes) {
}
@Override
protected void progress(BaseDownloadTask task, long soFarBytes, long totalBytes) {
send(context, (int) (soFarBytes*100.0/totalBytes),serverVersionName);
}
@Override
protected void paused(BaseDownloadTask task, long soFarBytes, long totalBytes) {
}
@Override
protected void completed(BaseDownloadTask task) {
send(context,100,serverVersionName);
}
@Override
protected void error(BaseDownloadTask task, Throwable e) {
Toast.makeText(context, "下载出错", Toast.LENGTH_SHORT).show();
}
@Override
protected void warn(BaseDownloadTask task) {
}
}).start();
}
private static void send(Context context,int progress,String serverVersionName) {
Intent intent = new Intent("teprinciple.update");
intent.putExtra("progress",progress);
intent.putExtra("title",serverVersionName);
context.sendBroadcast(intent);
}
}
================================================
FILE: UpdateAppUtils/src/main/java/util/UpdateAppReceiver.java
================================================
package util;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.FileProvider;
import java.io.File;
/**
* Created by Teprinciple on 2017/11/3.
*/
public class UpdateAppReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int notifyId = 1;
int progress = intent.getIntExtra("progress", 0);
String title = intent.getStringExtra("title");
NotificationManager nm = null;
if (UpdateAppUtils.showNotification){
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("正在下载 "+title);
builder.setSmallIcon(android.R.mipmap.sym_def_app_icon);
builder.setProgress(100,progress,false);
Notification notification = builder.build();
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(notifyId,notification);
}
if (progress == 100){
if (nm!=null)nm.cancel(notifyId);
if (DownloadAppUtils.downloadUpdateApkFilePath != null) {
Intent i = new Intent(Intent.ACTION_VIEW);
File apkFile = new File(DownloadAppUtils.downloadUpdateApkFilePath);
if ( UpdateAppUtils.needFitAndroidN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(
context, context.getPackageName() + ".fileprovider", apkFile);
i.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
i.setDataAndType(Uri.fromFile(apkFile),
"application/vnd.android.package-archive");
}
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
}
}
================================================
FILE: UpdateAppUtils/src/main/java/util/UpdateAppUtils.java
================================================
package util;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;
import android.util.Log;
import customview.ConfirmDialog;
import feature.Callback;
/**
* Created by Teprinciple on 2016/11/15.
*/
public class UpdateAppUtils {
private final String TAG = "UpdateAppUtils";
public static final int CHECK_BY_VERSION_NAME = 1001;
public static final int CHECK_BY_VERSION_CODE = 1002;
public static final int DOWNLOAD_BY_APP = 1003;
public static final int DOWNLOAD_BY_BROWSER = 1004;
private Activity activity;
private int checkBy = CHECK_BY_VERSION_CODE;
private int downloadBy = DOWNLOAD_BY_APP;
private int serverVersionCode = 0;
private String apkPath="";
private String serverVersionName="";
private boolean isForce = false; //是否强制更新
private int localVersionCode = 0;
private String localVersionName="";
public static boolean needFitAndroidN = true; //提供给 整个工程不需要适配到7.0的项目 置为false
public static boolean showNotification = true;
private String updateInfo = "";
public UpdateAppUtils needFitAndroidN(boolean needFitAndroidN) {
UpdateAppUtils.needFitAndroidN = needFitAndroidN;
return this;
}
private UpdateAppUtils(Activity activity) {
this.activity = activity;
getAPPLocalVersion(activity);
}
public static UpdateAppUtils from(Activity activity){
return new UpdateAppUtils(activity);
}
public UpdateAppUtils checkBy(int checkBy){
this.checkBy = checkBy;
return this;
}
public UpdateAppUtils apkPath(String apkPath){
this.apkPath = apkPath;
return this;
}
public UpdateAppUtils downloadBy(int downloadBy){
this.downloadBy = downloadBy;
return this;
}
public UpdateAppUtils showNotification(boolean showNotification){
this.showNotification = showNotification;
return this;
}
public UpdateAppUtils updateInfo(String updateInfo){
this.updateInfo = updateInfo;
return this;
}
public UpdateAppUtils serverVersionCode(int serverVersionCode){
this.serverVersionCode = serverVersionCode;
return this;
}
public UpdateAppUtils serverVersionName(String serverVersionName){
this.serverVersionName = serverVersionName;
return this;
}
public UpdateAppUtils isForce(boolean isForce){
this.isForce = isForce;
return this;
}
//获取apk的版本号 currentVersionCode
private void getAPPLocalVersion(Context ctx) {
PackageManager manager = ctx.getPackageManager();
try {
PackageInfo info = manager.getPackageInfo(ctx.getPackageName(), 0);
localVersionName = info.versionName; // 版本名
localVersionCode = info.versionCode; // 版本号
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
public void update(){
switch (checkBy){
case CHECK_BY_VERSION_CODE:
if (serverVersionCode >localVersionCode){
toUpdate();
}else {
Log.i(TAG,"当前版本是最新版本"+serverVersionCode+"/"+serverVersionName);
}
break;
case CHECK_BY_VERSION_NAME:
if (!serverVersionName.equals(localVersionName)){
toUpdate();
}else {
Log.i(TAG,"当前版本是最新版本"+serverVersionCode+"/"+serverVersionName);
}
break;
}
}
private void toUpdate() {
realUpdate();
}
private void realUpdate() {
ConfirmDialog dialog = new ConfirmDialog(activity, new Callback() {
@Override
public void callback(int position) {
switch (position){
case 0: //cancle
if (isForce)System.exit(0);
break;
case 1: //sure
if (downloadBy == DOWNLOAD_BY_APP) {
if (isWifiConnected(activity)){
// DownloadAppUtils.downloadForAutoInstall(activity, apkPath, "demo.apk", serverVersionName);
DownloadAppUtils.download(activity, apkPath, serverVersionName);
}else {
new ConfirmDialog(activity, new Callback() {
@Override
public void callback(int position) {
if (position==1){
DownloadAppUtils.download(activity, apkPath, serverVersionName);
//DownloadAppUtils.downloadForAutoInstall(activity, apkPath, "demo.apk", serverVersionName);
}else {
if (isForce)activity.finish();
}
}
}).setContent("目前手机不是WiFi状态\n确认是否继续下载更新?").show();
}
}else if (downloadBy == DOWNLOAD_BY_BROWSER){
DownloadAppUtils.downloadForWebView(activity,apkPath);
}
break;
}
}
});
String content = "发现新版本:"+serverVersionName+"\n是否下载更新?";
if (!TextUtils.isEmpty(updateInfo)){
content = "发现新版本:"+serverVersionName+"是否下载更新?\n\n"+updateInfo;
}
dialog .setContent(content);
dialog.setCancelable(false);
dialog.show();
}
/**
* 检测wifi是否连接
*/
public static boolean isWifiConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
return true;
}
}
return false;
}
}
================================================
FILE: UpdateAppUtils/src/main/res/drawable/bg_dialog_confirm.xml
================================================
================================================
FILE: UpdateAppUtils/src/main/res/layout/dialog_confirm.xml
================================================
================================================
FILE: UpdateAppUtils/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
#0076FF
#333333
================================================
FILE: UpdateAppUtils/src/main/res/values/strings.xml
================================================
Library
取消
确认
================================================
FILE: UpdateAppUtils/src/main/res/values/styles.xml
================================================
================================================
FILE: UpdateAppUtils/src/test/java/teprinciple/library/ExampleUnitTest.java
================================================
package teprinciple.library;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.example.teprinciple.updateappdemo"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile project(':updateapputils')
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
}
repositories {
mavenCentral()
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\Teprinciple\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/androidTest/java/com/example/teprinciple/updateappdemo/ExampleInstrumentedTest.java
================================================
package com.example.teprinciple.updateappdemo;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.example.teprinciple.updateappdemo", appContext.getPackageName());
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/example/teprinciple/updateappdemo/KotlinDemoActivity.kt
================================================
package com.example.teprinciple.updateappdemo
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import util.UpdateAppUtils
class KotlinDemoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kotlin_demo)
findViewById(R.id.kotlin_btn).setOnClickListener {
update()
}
}
private fun update() {
val apkPath:String = "http://issuecdn.baidupcs.com/issue/netdisk/apk/BaiduNetdisk_7.15.1.apk"
UpdateAppUtils.from(this)
.serverVersionCode(2)
.serverVersionName("2.0")
.apkPath(apkPath)
.update()
}
}
================================================
FILE: app/src/main/java/com/example/teprinciple/updateappdemo/MainActivity.java
================================================
package com.example.teprinciple.updateappdemo;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import customview.ConfirmDialog;
import feature.Callback;
import util.UpdateAppUtils;
public class MainActivity extends AppCompatActivity {
//服务器apk path,这里放了百度云盘的apk 作为测试
String apkPath = "http://issuecdn.baidupcs.com/issue/netdisk/apk/BaiduNetdisk_7.15.1.apk";
private int code = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void updateApp(View view) {
checkAndUpdate(1);
}
public void downloadByWeb(View view) {
checkAndUpdate(2);
}
public void forceUpdate(View view) {
checkAndUpdate(3);
}
public void checkByName(View view) {
checkAndUpdate(4);
}
public void kotlin(View view) {
checkAndUpdate(5);
}
private void checkAndUpdate(int code) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
realUpdate(code);
} else {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
realUpdate(code);
} else {//申请权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
}
}
private void realUpdate(int code) {
this.code = code;
switch (code) {
case 1:
updat1();
break;
case 2:
update2();
break;
case 3:
update3();
break;
case 4:
update4();
break;
case 5:
startActivity(new Intent(this,KotlinDemoActivity.class));
break;
}
}
//基本更新
private void updat1() {
UpdateAppUtils.from(this)
.serverVersionCode(2)
.serverVersionName("2.0")
.apkPath(apkPath)
.updateInfo("1.修复若干bug\n2.美化部分页面\n3.增加微信支付方式")
// .showNotification(false)
// .needFitAndroidN(false)
.update();
}
//通过浏览器下载
private void update2() {
UpdateAppUtils.from(this)
.serverVersionCode(2)
.serverVersionName("2.0")
.apkPath(apkPath)
.downloadBy(UpdateAppUtils.DOWNLOAD_BY_BROWSER)
.update();
}
//强制更新
private void update3() {
UpdateAppUtils.from(this)
.serverVersionCode(2)
.serverVersionName("2.0")
.apkPath(apkPath)
.isForce(true)
.update();
}
//根据versionName判断跟新
private void update4() {
UpdateAppUtils.from(this)
.checkBy(UpdateAppUtils.CHECK_BY_VERSION_NAME)
.serverVersionName("2.0")
.serverVersionCode(2)
.apkPath(apkPath)
.downloadBy(UpdateAppUtils.DOWNLOAD_BY_BROWSER)
.isForce(true)
.update();
}
//权限请求结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
realUpdate(code);
} else {
new ConfirmDialog(this, new Callback() {
@Override
public void callback(int position) {
if (position==1){
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName())); // 根据包名打开对应的设置界面
startActivity(intent);
}
}
}).setContent("暂无读写SD卡权限\n是否前往设置?").show();
}
break;
}
}
}
================================================
FILE: app/src/main/res/drawable/bg_dialog_confirm.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_kotlin_demo.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_start.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
#0076FF
#333333
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
16dp
16dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
UpdateAppDemo
取消
确认
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
64dp
================================================
FILE: app/src/main/res/xml/file_paths.xml
================================================
================================================
FILE: app/src/test/java/com/example/teprinciple/updateappdemo/ExampleUnitTest.java
================================================
package com.example.teprinciple.updateappdemo;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.1.4-3'
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
tasks.withType(Javadoc).all { enabled = false }
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app', ':updateapputils'