queue, String packageName, String updateXmlUrl, JSONObject options) {
46 | this.mContext = mContext;
47 | this.queue = queue;
48 | this.packageName = packageName;
49 | this.updateXmlUrl = updateXmlUrl;
50 | this.authentication = new AuthenticationOptions(options);
51 | this.mHandler = mHandler;
52 | }
53 |
54 | @Override
55 | public void run() {
56 | int versionCodeLocal = getVersionCodeLocal(mContext); // 获取当前软件版本
57 | int versionCodeRemote = getVersionCodeRemote(); //获取服务器当前软件版本
58 |
59 | queue.clear(); //ensure the queue is empty
60 | queue.add(new Version(versionCodeLocal, versionCodeRemote));
61 |
62 | if (versionCodeLocal == 0 || versionCodeRemote == 0) {
63 | mHandler.sendEmptyMessage(Constants.VERSION_RESOLVE_FAIL);
64 | } else {
65 | mHandler.sendEmptyMessage(Constants.VERSION_COMPARE_START);
66 | }
67 | }
68 |
69 | /**
70 | * 通过url返回文件
71 | *
72 | * @param path
73 | * @return
74 | */
75 | private InputStream returnFileIS(String path) {
76 | LOG.d(TAG, "returnFileIS..");
77 |
78 | URL url = null;
79 | InputStream is = null;
80 |
81 | try {
82 | url = new URL(path);
83 | HttpURLConnection conn = (HttpURLConnection) url.openConnection();//利用HttpURLConnection对象,我们可以从网络中获取网页数据.
84 |
85 | if(this.authentication.hasCredentials()){
86 | conn.setRequestProperty("Authorization", this.authentication.getEncodedAuthorization());
87 | }
88 |
89 | conn.setDoInput(true);
90 | conn.connect();
91 | is = conn.getInputStream(); //得到网络返回的输入流
92 | } catch (FileNotFoundException e) {
93 | e.printStackTrace();
94 | mHandler.sendEmptyMessage(Constants.REMOTE_FILE_NOT_FOUND);
95 | } catch (IOException e) {
96 | e.printStackTrace();
97 | mHandler.sendEmptyMessage(Constants.NETWORK_ERROR);
98 | }
99 |
100 | return is;
101 | }
102 |
103 | /**
104 | * 获取软件版本号
105 | *
106 | * It's weird, I don't know why.
107 | *
108 | * versionName -> versionCode
109 | * 0.0.1 -> 12
110 | * 0.3.4 -> 3042
111 | * 3.2.4 -> 302042
112 | * 12.234.221 -> 1436212
113 | *
114 | *
115 | * @param context
116 | * @return
117 | */
118 | private int getVersionCodeLocal(Context context) {
119 | LOG.d(TAG, "getVersionCode..");
120 |
121 | int versionCode = 0;
122 | try {
123 | // 获取软件版本号,对应AndroidManifest.xml下android:versionCode
124 | versionCode = context.getPackageManager().getPackageInfo(packageName, 0).versionCode;
125 | } catch (NameNotFoundException e) {
126 | e.printStackTrace();
127 | }
128 | return versionCode;
129 | }
130 |
131 | /**
132 | * 获取服务器软件版本号
133 | *
134 | * @return
135 | */
136 | private int getVersionCodeRemote() {
137 | int versionCodeRemote = 0;
138 |
139 | InputStream is = returnFileIS(updateXmlUrl);
140 | // 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析
141 | ParseXmlService service = new ParseXmlService();
142 | try {
143 | setMHashMap(service.parseXml(is));
144 | } catch (Exception e) {
145 | e.printStackTrace();
146 | }
147 | if (null != getMHashMap()) {
148 | versionCodeRemote = Integer.valueOf(getMHashMap().get("version"));
149 | }
150 |
151 | return versionCodeRemote;
152 | }
153 | }
--------------------------------------------------------------------------------
/src/android/Constants.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | /**
4 | * Created by LuoWen on 2015/12/14.
5 | */
6 | public interface Constants {
7 | /* 下载中 */
8 | int DOWNLOAD = 1;
9 | /* 下载结束 */
10 | int DOWNLOAD_FINISH = 2;
11 | /* 点击开始下载按钮*/
12 | int DOWNLOAD_CLICK_START = 3;
13 |
14 | /**
15 | * 对比版本号
16 | */
17 | int VERSION_COMPARE_START = 200; //private 开始对比版本号; start to compare version
18 | int VERSION_NEED_UPDATE = 201; //检查到需要更新; need update
19 | int VERSION_UP_TO_UPDATE = 202; //软件是不需要更新;version up to date
20 | int VERSION_UPDATING = 203; //软件正在更新;version is updating
21 |
22 | /**
23 | * 版本解析错误
24 | */
25 | int VERSION_RESOLVE_FAIL = 301; //版本文件解析错误 version-xml file resolve fail
26 | int VERSION_COMPARE_FAIL = 302; //版本文件对比错误 version-xml file compare fail
27 |
28 | /**
29 | * 网络错误
30 | */
31 | int REMOTE_FILE_NOT_FOUND = 404;
32 | int NETWORK_ERROR = 405;
33 |
34 | /**
35 | * 没有相应的方法
36 | */
37 | int NO_SUCH_METHOD = 501;
38 |
39 | /**
40 | * Permissions
41 | */
42 | int PERMISSION_DENIED = 601;
43 |
44 | /**
45 | * 未知错误
46 | */
47 | int UNKNOWN_ERROR = 901;
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/android/DownloadApkThread.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | import android.AuthenticationOptions;
4 | import android.app.AlertDialog;
5 | import android.content.Context;
6 | import android.os.Environment;
7 | import android.os.Handler;
8 | import android.widget.ProgressBar;
9 | import android.util.Base64;
10 | import org.json.JSONObject;
11 | import org.json.JSONException;
12 |
13 | import java.io.File;
14 | import java.io.FileOutputStream;
15 | import java.io.IOException;
16 | import java.io.InputStream;
17 | import java.net.HttpURLConnection;
18 | import java.net.MalformedURLException;
19 | import java.net.URL;
20 | import java.util.HashMap;
21 |
22 | import java.nio.charset.StandardCharsets;
23 |
24 | /**
25 | * 下载文件线程
26 | */
27 | public class DownloadApkThread implements Runnable {
28 | private String TAG = "DownloadApkThread";
29 |
30 | /* 保存解析的XML信息 */
31 | HashMap mHashMap;
32 | /* 下载保存路径 */
33 | private String mSavePath;
34 | /* 记录进度条数量 */
35 | private int progress;
36 | /* 是否取消更新 */
37 | private boolean cancelUpdate = false;
38 | private AlertDialog mDownloadDialog;
39 | private DownloadHandler downloadHandler;
40 | private Handler mHandler;
41 | private AuthenticationOptions authentication;
42 |
43 | public DownloadApkThread(Context mContext, Handler mHandler, ProgressBar mProgress, AlertDialog mDownloadDialog, HashMap mHashMap, JSONObject options) {
44 | this.mDownloadDialog = mDownloadDialog;
45 | this.mHashMap = mHashMap;
46 | this.mHandler = mHandler;
47 | this.authentication = new AuthenticationOptions(options);
48 |
49 | this.mSavePath = Environment.getExternalStorageDirectory() + "/" + "download"; // SD Path
50 | this.downloadHandler = new DownloadHandler(mContext, mProgress, mDownloadDialog, this.mSavePath, mHashMap);
51 | }
52 |
53 |
54 | @Override
55 | public void run() {
56 | downloadAndInstall();
57 | // 取消下载对话框显示
58 | // mDownloadDialog.dismiss();
59 | }
60 |
61 | public void cancelBuildUpdate() {
62 | this.cancelUpdate = true;
63 | }
64 |
65 | private void downloadAndInstall() {
66 | try {
67 | // 判断SD卡是否存在,并且是否具有读写权限
68 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
69 | // 获得存储卡的路径
70 | URL url = new URL(mHashMap.get("url"));
71 | // 创建连接
72 | HttpURLConnection conn = (HttpURLConnection) url.openConnection();
73 |
74 | if(this.authentication.hasCredentials()){
75 | conn.setRequestProperty("Authorization", this.authentication.getEncodedAuthorization());
76 | }
77 |
78 | conn.connect();
79 | // 获取文件大小
80 | int length = conn.getContentLength();
81 | // 创建输入流
82 | InputStream is = conn.getInputStream();
83 |
84 | File file = new File(mSavePath);
85 | // 判断文件目录是否存在
86 | if (!file.exists()) {
87 | file.mkdir();
88 | }
89 | File apkFile = new File(mSavePath, mHashMap.get("name")+".apk");
90 | FileOutputStream fos = new FileOutputStream(apkFile);
91 | int count = 0;
92 | // 缓存
93 | byte buf[] = new byte[1024];
94 |
95 | // 写入到文件中
96 | do {
97 | int numread = is.read(buf);
98 | count += numread;
99 | // 计算进度条位置
100 | progress = (int) (((float) count / length) * 100);
101 | downloadHandler.updateProgress(progress);
102 | // 更新进度
103 | downloadHandler.sendEmptyMessage(Constants.DOWNLOAD);
104 | if (numread <= 0) {
105 | // 下载完成
106 | downloadHandler.sendEmptyMessage(Constants.DOWNLOAD_FINISH);
107 | mHandler.sendEmptyMessage(Constants.DOWNLOAD_FINISH);
108 | break;
109 | }
110 | // 写入文件
111 | fos.write(buf, 0, numread);
112 | } while (!cancelUpdate);// 点击取消就停止下载.
113 | fos.close();
114 | is.close();
115 | }
116 | } catch (MalformedURLException e) {
117 | e.printStackTrace();
118 | } catch (IOException e) {
119 | e.printStackTrace();
120 | }
121 |
122 | }
123 | }
--------------------------------------------------------------------------------
/src/android/DownloadHandler.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | import org.apache.cordova.BuildHelper;
4 |
5 | import android.app.AlertDialog;
6 | import android.app.Activity;
7 | import android.content.Context;
8 | import android.content.DialogInterface;
9 | import android.content.Intent;
10 | import android.net.Uri;
11 | import android.os.Build;
12 | import android.os.Handler;
13 | import android.os.Message;
14 | import android.view.View;
15 | import android.view.View.OnClickListener;
16 | import android.widget.ProgressBar;
17 | import android.support.v4.content.FileProvider;
18 | import java.io.File;
19 | import java.util.HashMap;
20 |
21 | import org.apache.cordova.LOG;
22 |
23 | /**
24 | * Created by LuoWen on 2015/12/14.
25 | */
26 | public class DownloadHandler extends Handler {
27 | private String TAG = "DownloadHandler";
28 |
29 | private Context mContext;
30 | /* 更新进度条 */
31 | private ProgressBar mProgress;
32 | /* 记录进度条数量 */
33 | private int progress;
34 | /* 下载保存路径 */
35 | private String mSavePath;
36 | /* 保存解析的XML信息 */
37 | private HashMap mHashMap;
38 | private MsgHelper msgHelper;
39 | private AlertDialog mDownloadDialog;
40 |
41 | public DownloadHandler(Context mContext, ProgressBar mProgress, AlertDialog mDownloadDialog, String mSavePath, HashMap mHashMap) {
42 | this.msgHelper = new MsgHelper(mContext.getPackageName(), mContext.getResources());
43 | this.mDownloadDialog = mDownloadDialog;
44 | this.mContext = mContext;
45 | this.mProgress = mProgress;
46 | this.mSavePath = mSavePath;
47 | this.mHashMap = mHashMap;
48 | }
49 |
50 | public void handleMessage(Message msg) {
51 | switch (msg.what) {
52 | // 正在下载
53 | case Constants.DOWNLOAD:
54 | // 设置进度条位置
55 | mProgress.setProgress(progress);
56 | break;
57 | case Constants.DOWNLOAD_FINISH:
58 | updateMsgDialog();
59 | // 安装文件
60 | installApk();
61 | break;
62 | default:
63 | break;
64 | }
65 | }
66 |
67 | public void updateProgress(int progress) {
68 | this.progress = progress;
69 | }
70 |
71 | public void updateMsgDialog() {
72 | mDownloadDialog.setTitle(msgHelper.getString(MsgHelper.DOWNLOAD_COMPLETE_TITLE));
73 | if (mDownloadDialog.isShowing()) {
74 | mDownloadDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); //Update in background
75 | mDownloadDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setVisibility(View.VISIBLE); //Install Manually
76 | mDownloadDialog.getButton(DialogInterface.BUTTON_POSITIVE).setVisibility(View.VISIBLE); //Download Again
77 |
78 | mDownloadDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener(downloadCompleteOnClick);
79 | }
80 | }
81 |
82 | private OnClickListener downloadCompleteOnClick = new OnClickListener() {
83 | @Override
84 | public void onClick(View view) {
85 | installApk();
86 | }
87 | };
88 |
89 | /**
90 | * 安装APK文件
91 | */
92 | private void installApk() {
93 | LOG.d(TAG, "Installing APK");
94 |
95 | File apkFile = new File(mSavePath, mHashMap.get("name")+".apk");
96 | if (!apkFile.exists()) {
97 | LOG.e(TAG, "Could not find APK: " + mHashMap.get("name"));
98 | return;
99 | }
100 |
101 | LOG.d(TAG, "APK Filename: " + apkFile.toString());
102 |
103 | // 通过Intent安装APK文件
104 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
105 | LOG.d(TAG, "Build SDK Greater than or equal to Nougat");
106 | String applicationId = (String) BuildHelper.getBuildConfigValue((Activity) mContext, "APPLICATION_ID");
107 | Uri apkUri = FileProvider.getUriForFile(mContext, applicationId + ".appupdate.provider", apkFile);
108 | Intent i = new Intent(Intent.ACTION_INSTALL_PACKAGE);
109 | i.setData(apkUri);
110 | i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
111 | mContext.startActivity(i);
112 | }else{
113 | LOG.d(TAG, "Build SDK less than Nougat");
114 | Intent i = new Intent(Intent.ACTION_VIEW);
115 | i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
116 | i.setDataAndType(Uri.parse("file://" + apkFile.toString()), "application/vnd.android.package-archive");
117 | mContext.startActivity(i);
118 | }
119 |
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/android/GenericFileProvider.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | import android.support.v4.content.FileProvider;
4 |
5 | /**
6 | * Created by kalea on 2017/8/23.
7 | */
8 |
9 | public class GenericFileProvider extends FileProvider {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/android/GetRemoteXmlThread.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | /**
4 | * Created by LuoWen on 2015/10/27.
5 | */
6 | public class GetRemoteXmlThread extends Thread {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/android/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Wen Luo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/src/android/MsgBox.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.AlertDialog.Builder;
5 | import android.app.Dialog;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.content.DialogInterface.OnClickListener;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.widget.ProgressBar;
12 | import org.apache.cordova.LOG;
13 |
14 | import java.util.HashMap;
15 | import java.util.Map;
16 |
17 | /**
18 | * Created by LuoWen on 2016/1/20.
19 | */
20 | public class MsgBox {
21 | public static final String TAG = "MsgBox";
22 | private Context mContext;
23 | private MsgHelper msgHelper;
24 |
25 | private Dialog noticeDialog;
26 | private AlertDialog downloadDialog;
27 | private ProgressBar downloadDialogProgress;
28 | private Dialog errorDialog;
29 |
30 | public MsgBox(Context mContext) {
31 | this.mContext = mContext;
32 | this.msgHelper = new MsgHelper(mContext.getPackageName(), mContext.getResources());
33 | }
34 |
35 | /**
36 | * 显示软件更新对话框
37 | *
38 | * @param onClickListener
39 | */
40 | public Dialog showNoticeDialog(OnClickListener onClickListener) {
41 | if (noticeDialog == null) {
42 | LOG.d(TAG, "showNoticeDialog");
43 | // 构造对话框
44 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
45 | builder.setTitle(msgHelper.getString(MsgHelper.UPDATE_TITLE));
46 | builder.setMessage(msgHelper.getString(MsgHelper.UPDATE_MESSAGE));
47 | // 更新
48 | builder.setPositiveButton(msgHelper.getString(MsgHelper.UPDATE_UPDATE_BTN), onClickListener);
49 | noticeDialog = builder.create();
50 | }
51 |
52 | if (!noticeDialog.isShowing()) noticeDialog.show();
53 |
54 | noticeDialog.setCanceledOnTouchOutside(false);// 设置点击屏幕Dialog不消失
55 | return noticeDialog;
56 | }
57 |
58 |
59 | /**
60 | * 显示软件下载对话框
61 | */
62 | public Map showDownloadDialog(OnClickListener onClickListenerNeg,
63 | OnClickListener onClickListenerPos,
64 | OnClickListener onClickListenerNeu,
65 | boolean showDialog) {
66 | if (downloadDialog == null) {
67 | LOG.d(TAG, "showDownloadDialog");
68 |
69 | // 构造软件下载对话框
70 | AlertDialog.Builder builder = new Builder(mContext);
71 | builder.setTitle(msgHelper.getString(MsgHelper.UPDATING));
72 | // 给下载对话框增加进度条
73 | final LayoutInflater inflater = LayoutInflater.from(mContext);
74 | View v = inflater.inflate(msgHelper.getLayout(MsgHelper.APPUPDATE_PROGRESS), null);
75 |
76 | /* 更新进度条 */
77 | downloadDialogProgress = (ProgressBar) v.findViewById(msgHelper.getId(MsgHelper.UPDATE_PROGRESS));
78 | builder.setView(v);
79 | // 取消更新
80 | //builder.setNegativeButton(msgHelper.getString("update_cancel"), onClickListener);
81 | //转到后台更新
82 | builder.setNegativeButton(msgHelper.getString(MsgHelper.UPDATE_BG), onClickListenerNeg);
83 | builder.setNeutralButton(msgHelper.getString(MsgHelper.DOWNLOAD_COMPLETE_NEU_BTN), onClickListenerNeu);
84 | builder.setPositiveButton(msgHelper.getString(MsgHelper.DOWNLOAD_COMPLETE_POS_BTN), onClickListenerPos);
85 | downloadDialog = builder.create();
86 | }
87 |
88 | if (showDialog && !downloadDialog.isShowing()) downloadDialog.show();
89 |
90 | downloadDialog.setTitle(msgHelper.getString(MsgHelper.UPDATING));
91 | downloadDialog.setCanceledOnTouchOutside(false);// 设置点击屏幕Dialog不消失
92 | if (downloadDialog.isShowing()) {
93 | downloadDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.VISIBLE); //Update in background
94 | downloadDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setVisibility(View.GONE); //Install Manually
95 | downloadDialog.getButton(DialogInterface.BUTTON_POSITIVE).setVisibility(View.GONE); //Download Again
96 | }
97 |
98 | Map ret = new HashMap();
99 | ret.put("dialog", downloadDialog);
100 | ret.put("progress", downloadDialogProgress);
101 | return ret;
102 | }
103 |
104 | /**
105 | * 错误提示窗口
106 | *
107 | * @param errorDialogOnClick
108 | */
109 | public Dialog showErrorDialog(OnClickListener errorDialogOnClick) {
110 | if (this.errorDialog == null) {
111 | LOG.d(TAG, "initErrorDialog");
112 | // 构造对话框
113 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
114 | builder.setTitle(msgHelper.getString(MsgHelper.UPDATE_ERROR_TITLE));
115 | builder.setMessage(msgHelper.getString(MsgHelper.UPDATE_ERROR_MESSAGE));
116 | // 更新
117 | builder.setPositiveButton(msgHelper.getString(MsgHelper.UPDATE_ERROR_YES_BTN), errorDialogOnClick);
118 | errorDialog = builder.create();
119 | }
120 |
121 | if (!errorDialog.isShowing()) errorDialog.show();
122 |
123 | return errorDialog;
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/src/android/MsgHelper.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | import android.content.res.Resources;
4 |
5 | /**
6 | * Created by LuoWen on 16/9/16.
7 | */
8 | public class MsgHelper {
9 | private String packageName;
10 | private Resources resources;
11 |
12 | public static String UPDATE_TITLE = "update_title";
13 | public static String UPDATE_MESSAGE = "update_message";
14 | public static String UPDATE_UPDATE_BTN = "update_update_btn";
15 | public static String APPUPDATE_PROGRESS = "appupdate_progress";
16 | public static String UPDATE_PROGRESS = "update_progress";
17 | public static String UPDATING = "updating";
18 | public static String UPDATE_BG = "update_bg";
19 | public static String DOWNLOAD_COMPLETE_TITLE = "download_complete_title";
20 | public static String DOWNLOAD_COMPLETE_POS_BTN = "download_complete_pos_btn";
21 | public static String DOWNLOAD_COMPLETE_NEU_BTN = "download_complete_neu_btn";
22 | public static String UPDATE_ERROR_TITLE = "update_error_title";
23 | public static String UPDATE_ERROR_MESSAGE = "update_error_message";
24 | public static String UPDATE_ERROR_YES_BTN = "update_error_yes_btn";
25 |
26 |
27 | MsgHelper(String packageName, Resources resources) {
28 | this.packageName = packageName;
29 | this.resources = resources;
30 | }
31 |
32 | public int getId(String name) {
33 | return resources.getIdentifier(name, "id", packageName);
34 | }
35 |
36 | public int getString(String name) {
37 | return resources.getIdentifier(name, "string", packageName);
38 | }
39 |
40 | public int getLayout(String name) {
41 | return resources.getIdentifier(name, "layout", packageName);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/android/ParseXmlService.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | import org.w3c.dom.Document;
4 | import org.w3c.dom.Element;
5 | import org.w3c.dom.Node;
6 | import org.w3c.dom.NodeList;
7 |
8 | import javax.xml.parsers.DocumentBuilder;
9 | import javax.xml.parsers.DocumentBuilderFactory;
10 | import java.io.InputStream;
11 | import java.util.HashMap;
12 |
13 | /**
14 | * Created by LuoWen on 2015/10/27.
15 | */
16 | public class ParseXmlService {
17 | public HashMap parseXml(InputStream inStream) throws Exception {
18 | HashMap hashMap = new HashMap();
19 |
20 | // 实例化一个文档构建器工厂
21 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
22 | // 通过文档构建器工厂获取一个文档构建器
23 | DocumentBuilder builder = factory.newDocumentBuilder();
24 | // 通过文档通过文档构建器构建一个文档实例
25 | Document document = builder.parse(inStream);
26 | //获取XML文件根节点
27 | Element root = document.getDocumentElement();
28 | //获得所有子节点
29 | NodeList childNodes = root.getChildNodes();
30 | for (int j = 0; j < childNodes.getLength(); j++) {
31 | //遍历子节点
32 | Node childNode = (Node) childNodes.item(j);
33 | if (childNode.getNodeType() == Node.ELEMENT_NODE) {
34 | Element childElement = (Element) childNode;
35 | //版本号
36 | if ("version".equals(childElement.getNodeName())) {
37 | hashMap.put("version", childElement.getFirstChild().getNodeValue());
38 | }
39 | //软件名称
40 | else if (("name".equals(childElement.getNodeName()))) {
41 | hashMap.put("name", childElement.getFirstChild().getNodeValue());
42 | }
43 | //下载地址
44 | else if (("url".equals(childElement.getNodeName()))) {
45 | hashMap.put("url", childElement.getFirstChild().getNodeValue());
46 | }
47 | }
48 | }
49 | return hashMap;
50 | }
51 | }
--------------------------------------------------------------------------------
/src/android/UIValues.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | /**
4 | * Created by Administrator on 2016/1/20.
5 | */
6 | public class UIValues {
7 | private String updateLatest;
8 | private String updateTitle;
9 | private String updateMessage;
10 | private String updateUpdateBtn;
11 | private String updating;
12 | private String updateCancel;
13 | private String updateErrorTitle;
14 | private String updateErrorMessage;
15 | private String updateErrorYesBtn;
16 |
17 | public String getUpdateLatest() {
18 | return updateLatest;
19 | }
20 |
21 | public void setUpdateLatest(String updateLatest) {
22 | this.updateLatest = updateLatest;
23 | }
24 |
25 | public String getUpdateTitle() {
26 | return updateTitle;
27 | }
28 |
29 | public void setUpdateTitle(String updateTitle) {
30 | this.updateTitle = updateTitle;
31 | }
32 |
33 | public String getUpdateMessage() {
34 | return updateMessage;
35 | }
36 |
37 | public void setUpdateMessage(String updateMessage) {
38 | this.updateMessage = updateMessage;
39 | }
40 |
41 | public String getUpdateUpdateBtn() {
42 | return updateUpdateBtn;
43 | }
44 |
45 | public void setUpdateUpdateBtn(String updateUpdateBtn) {
46 | this.updateUpdateBtn = updateUpdateBtn;
47 | }
48 |
49 | public String getUpdating() {
50 | return updating;
51 | }
52 |
53 | public void setUpdating(String updating) {
54 | this.updating = updating;
55 | }
56 |
57 | public String getUpdateCancel() {
58 | return updateCancel;
59 | }
60 |
61 | public void setUpdateCancel(String updateCancel) {
62 | this.updateCancel = updateCancel;
63 | }
64 |
65 | public String getUpdateErrorTitle() {
66 | return updateErrorTitle;
67 | }
68 |
69 | public void setUpdateErrorTitle(String updateErrorTitle) {
70 | this.updateErrorTitle = updateErrorTitle;
71 | }
72 |
73 | public String getUpdateErrorMessage() {
74 | return updateErrorMessage;
75 | }
76 |
77 | public void setUpdateErrorMessage(String updateErrorMessage) {
78 | this.updateErrorMessage = updateErrorMessage;
79 | }
80 |
81 | public String getUpdateErrorYesBtn() {
82 | return updateErrorYesBtn;
83 | }
84 |
85 | public void setUpdateErrorYesBtn(String updateErrorYesBtn) {
86 | this.updateErrorYesBtn = updateErrorYesBtn;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/android/UpdateManager.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.content.DialogInterface.OnClickListener;
7 | import android.os.Handler;
8 | import android.widget.ProgressBar;
9 | import org.apache.cordova.CallbackContext;
10 | import org.apache.cordova.CordovaInterface;
11 | import org.apache.cordova.LOG;
12 | import org.json.JSONArray;
13 | import org.json.JSONObject;
14 | import org.json.JSONException;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 | import java.util.Map;
19 |
20 | /**
21 | * Created by LuoWen on 2015/10/27.
22 | *
23 | * Thanks @coolszy
24 | */
25 | public class UpdateManager {
26 | public static final String TAG = "UpdateManager";
27 |
28 | /*
29 | * 远程的版本文件格式
30 | *
31 | * 2222
32 | * name
33 | * http://192.168.3.102/android.apk
34 | *
35 | */
36 | private String updateXmlUrl;
37 | private JSONObject options;
38 | private JSONArray args;
39 | private CordovaInterface cordova;
40 | private CallbackContext callbackContext;
41 | private String packageName;
42 | private Context mContext;
43 | private MsgBox msgBox;
44 | private Boolean isDownloading = false;
45 | private List queue = new ArrayList(1);
46 | private CheckUpdateThread checkUpdateThread;
47 | private DownloadApkThread downloadApkThread;
48 |
49 | public UpdateManager(Context context, CordovaInterface cordova) {
50 | this.cordova = cordova;
51 | this.mContext = context;
52 | packageName = mContext.getPackageName();
53 | msgBox = new MsgBox(mContext);
54 | }
55 |
56 | public UpdateManager(JSONArray args, CallbackContext callbackContext, Context context, JSONObject options) {
57 | this(args, callbackContext, context, "http://192.168.3.102:8080/update_apk/version.xml", options);
58 | }
59 |
60 | public UpdateManager(JSONArray args, CallbackContext callbackContext, Context context, String updateUrl, JSONObject options) {
61 | this.args = args;
62 | this.callbackContext = callbackContext;
63 | this.updateXmlUrl = updateUrl;
64 | this.options = options;
65 | this.mContext = context;
66 | packageName = mContext.getPackageName();
67 | msgBox = new MsgBox(mContext);
68 | }
69 |
70 | public UpdateManager options(JSONArray args, CallbackContext callbackContext)
71 | throws JSONException {
72 | this.args = args;
73 | this.callbackContext = callbackContext;
74 | this.updateXmlUrl = args.getString(0);
75 | this.options = args.getJSONObject(1);
76 | return this;
77 | }
78 |
79 | private Handler mHandler = new Handler() {
80 | @Override
81 | public void handleMessage(android.os.Message msg) {
82 | super.handleMessage(msg);
83 |
84 | switch (msg.what) {
85 | case Constants.NETWORK_ERROR:
86 | //暂时隐藏错误
87 | //msgBox.showErrorDialog(errorDialogOnClick);
88 | callbackContext.error(Utils.makeJSON(Constants.NETWORK_ERROR, "network error"));
89 | break;
90 | case Constants.VERSION_COMPARE_START:
91 | compareVersions();
92 | break;
93 | case Constants.DOWNLOAD_CLICK_START:
94 | emitNoticeDialogOnClick();
95 | break;
96 | case Constants.DOWNLOAD_FINISH:
97 | isDownloading = false;
98 | break;
99 | case Constants.VERSION_UPDATING:
100 | callbackContext.success(Utils.makeJSON(Constants.VERSION_UPDATING, "success, version updating."));
101 | break;
102 | case Constants.VERSION_NEED_UPDATE:
103 | callbackContext.success(Utils.makeJSON(Constants.VERSION_NEED_UPDATE, "success, need date."));
104 | break;
105 | case Constants.VERSION_UP_TO_UPDATE:
106 | callbackContext.success(Utils.makeJSON(Constants.VERSION_UP_TO_UPDATE, "success, up to date."));
107 | break;
108 | case Constants.VERSION_COMPARE_FAIL:
109 | callbackContext.error(Utils.makeJSON(Constants.VERSION_COMPARE_FAIL, "version compare fail"));
110 | break;
111 | case Constants.VERSION_RESOLVE_FAIL:
112 | callbackContext.error(Utils.makeJSON(Constants.VERSION_RESOLVE_FAIL, "version resolve fail"));
113 | break;
114 | case Constants.REMOTE_FILE_NOT_FOUND:
115 | callbackContext.error(Utils.makeJSON(Constants.REMOTE_FILE_NOT_FOUND, "remote file not found"));
116 | break;
117 | default:
118 | callbackContext.error(Utils.makeJSON(Constants.UNKNOWN_ERROR, "unknown error"));
119 | }
120 |
121 | }
122 | };
123 |
124 | /**
125 | * 检测软件更新
126 | */
127 | public void checkUpdate() {
128 | LOG.d(TAG, "checkUpdate..");
129 |
130 | checkUpdateThread = new CheckUpdateThread(mContext, mHandler, queue, packageName, updateXmlUrl, options);
131 | this.cordova.getThreadPool().execute(checkUpdateThread);
132 | //new Thread(checkUpdateThread).start();
133 | }
134 |
135 | /**
136 | * Permissions denied
137 | */
138 | public void permissionDenied(String errMsg) {
139 | LOG.d(TAG, "permissionsDenied..");
140 |
141 | callbackContext.error(Utils.makeJSON(Constants.PERMISSION_DENIED, errMsg));
142 | }
143 |
144 | /**
145 | * 对比版本号
146 | */
147 | private void compareVersions() {
148 | Version version = queue.get(0);
149 | int versionCodeLocal = version.getLocal();
150 | int versionCodeRemote = version.getRemote();
151 |
152 | boolean skipPromptDialog = false;
153 | try {
154 | skipPromptDialog = options.getBoolean("skipPromptDialog");
155 | } catch (JSONException e) {}
156 |
157 | boolean skipProgressDialog = false;
158 | try {
159 | skipProgressDialog = options.getBoolean("skipProgressDialog");
160 | } catch (JSONException e) {}
161 |
162 | //比对版本号
163 | //检查软件是否有更新版本
164 | if (versionCodeLocal < versionCodeRemote) {
165 | if (isDownloading) {
166 | msgBox.showDownloadDialog(null, null, null, !skipProgressDialog);
167 | mHandler.sendEmptyMessage(Constants.VERSION_UPDATING);
168 | } else {
169 | LOG.d(TAG, "need update");
170 | if (skipPromptDialog) {
171 | mHandler.sendEmptyMessage(Constants.DOWNLOAD_CLICK_START);
172 | } else {
173 | // 显示提示对话框
174 | msgBox.showNoticeDialog(noticeDialogOnClick);
175 | mHandler.sendEmptyMessage(Constants.VERSION_NEED_UPDATE);
176 | }
177 | }
178 | } else {
179 | mHandler.sendEmptyMessage(Constants.VERSION_UP_TO_UPDATE);
180 | // Do not show Toast
181 | //Toast.makeText(mContext, getString("update_latest"), Toast.LENGTH_LONG).show();
182 | }
183 | }
184 |
185 | private OnClickListener noticeDialogOnClick = new OnClickListener() {
186 | @Override
187 | public void onClick(DialogInterface dialog, int which) {
188 | dialog.dismiss();
189 | mHandler.sendEmptyMessage(Constants.DOWNLOAD_CLICK_START);
190 | }
191 | };
192 |
193 | private void emitNoticeDialogOnClick() {
194 | isDownloading = true;
195 |
196 | boolean skipProgressDialog = false;
197 | try {
198 | skipProgressDialog = options.getBoolean("skipProgressDialog");
199 | } catch (JSONException e) {}
200 |
201 | // 显示下载对话框
202 | Map ret = msgBox.showDownloadDialog(
203 | downloadDialogOnClickNeg,
204 | downloadDialogOnClickPos,
205 | downloadDialogOnClickNeu,
206 | !skipProgressDialog);
207 |
208 | // 下载文件
209 | downloadApk((AlertDialog) ret.get("dialog"), (ProgressBar) ret.get("progress"));
210 | }
211 |
212 | /**
213 | * 手动安装
214 | * Download again
215 | */
216 | private OnClickListener downloadDialogOnClickNeu = new OnClickListener() {
217 | @Override
218 | public void onClick(DialogInterface dialog, int which) {
219 | //Implemented in DownloadHandler.java
220 | }
221 | };
222 | /**
223 | * 重新下载
224 | * Download again
225 | */
226 | private OnClickListener downloadDialogOnClickPos = new OnClickListener() {
227 | @Override
228 | public void onClick(DialogInterface dialog, int which) {
229 | dialog.dismiss();
230 | mHandler.sendEmptyMessage(Constants.DOWNLOAD_CLICK_START);
231 | }
232 | };
233 | /**
234 | * 转到后台更新
235 | * Update in background
236 | */
237 | private OnClickListener downloadDialogOnClickNeg = new OnClickListener() {
238 | @Override
239 | public void onClick(DialogInterface dialog, int which) {
240 | dialog.dismiss();
241 | // 设置取消状态
242 | //downloadApkThread.cancelBuildUpdate();
243 | }
244 | };
245 |
246 | private OnClickListener errorDialogOnClick = new OnClickListener() {
247 | @Override
248 | public void onClick(DialogInterface dialog, int which) {
249 | dialog.dismiss();
250 | }
251 | };
252 |
253 | /**
254 | * 下载apk文件
255 | *
256 | * @param mProgress
257 | * @param mDownloadDialog
258 | */
259 | private void downloadApk(AlertDialog mDownloadDialog, ProgressBar mProgress) {
260 | LOG.d(TAG, "downloadApk" + mProgress);
261 |
262 | // 启动新线程下载软件
263 | downloadApkThread = new DownloadApkThread(mContext, mHandler, mProgress, mDownloadDialog, checkUpdateThread.getMHashMap(), options);
264 | this.cordova.getThreadPool().execute(downloadApkThread);
265 | // new Thread(downloadApkThread).start();
266 | }
267 |
268 | }
269 |
--------------------------------------------------------------------------------
/src/android/Utils.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | /**
7 | * Created by LuoWen on 16/7/22.
8 | */
9 | public class Utils {
10 |
11 | static JSONObject makeJSON(int code, Object msg) {
12 | JSONObject json = new JSONObject();
13 |
14 | try {
15 | json.put("code", code);
16 | json.put("msg", msg);
17 | } catch (JSONException e) {
18 | e.printStackTrace();
19 | }
20 |
21 | return json;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/android/Version.java:
--------------------------------------------------------------------------------
1 | package com.vaenow.appupdate.android;
2 |
3 | /**
4 | * Created by LuoWen on 2015/12/14.
5 | */
6 | public class Version {
7 | private int local;
8 | private int remote;
9 |
10 | public Version(int local, int remote) {
11 | this.local = local;
12 | this.remote = remote;
13 | }
14 |
15 | public int getLocal() {
16 | return local;
17 | }
18 |
19 | public int getRemote() {
20 | return remote;
21 | }
22 | }
--------------------------------------------------------------------------------
/syncFromDemo.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # sync *.java
4 | cp -r ../cordova-plugin-app-update-demo/platforms/android/src/com/vaenow/appupdate/android/* src/android/
5 |
6 | # sync *.xml
7 | cp -r ../cordova-plugin-app-update-demo/platforms/android/res/values* res/
8 |
9 | # clean
10 | rm res/values/strings.xml
11 |
12 |
--------------------------------------------------------------------------------
/www/AppUpdate.js:
--------------------------------------------------------------------------------
1 | var exec = require('cordova/exec');
2 |
3 | /**
4 | * Check if there is an update to the App.
5 | *
6 | * This function can be called in three ways:
7 | * 1. checkAppUpdate(updateUrl)
8 | * 2. checkAppUpdate(updateUrl, options)
9 | * 3. checkAppUpdate(sucessCallback, errorCallback, updateUrl, options)
10 | *
11 | * @param successOrUrl The success callback or the URL where the update data is located
12 | * @param errorOrOptions The function called on error or the authentication options
13 | * @param updateUrl The URL where the update data is located
14 | * @param options An object that may contain the authentication options
15 | */
16 | exports.checkAppUpdate = function(successOrUrl, errorOrOptions, updateUrl, options) {
17 | // If the update URL hasnt been set in the updateUrl then assume no callbacks passed
18 | var successCallback = updateUrl ? successOrUrl : null;
19 | var errorCallback = updateUrl ? errorOrOptions : null;
20 |
21 | // This handles case 2, where there is an updateURL and options set
22 | if ( !updateUrl && typeof errorOrOptions === 'object' ) {
23 | options = errorOrOptions;
24 | }
25 |
26 | // If there is no updateUrl then assume that the URL is the first paramater
27 | updateUrl = updateUrl ? updateUrl : successOrUrl;
28 |
29 | options = options ? options : {};
30 |
31 | exec(successCallback, errorCallback, "AppUpdate", "checkAppUpdate", [updateUrl, options]);
32 | };
33 |
--------------------------------------------------------------------------------