提交 65b5e517 authored 作者: 吴强's avatar 吴强

Merge branch 'master' of ssh://192.168.1.116:29418/bolan-root/frontend/manager-app-sz into wq

# Conflicts: # android/app/src/main/java/com/bolanmanagerapp/MainApplication.java # src/utils/config.js
node_modules/
.expo/
ios/build/
android/.gradle/
android/build/
android/app/build/
npm-debug.*
*.iml
.idea/
.idea
.gradle
.DS_Store
npm-debug.log
yarn-error.log
......
package com.bolan.android.modules;
import com.bolan.android.modules.update.Updater;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class UpdaterReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new Updater(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
package com.bolan.android.modules;
import android.content.Context;
import android.os.Environment;
import java.io.File;
public class Utils {
public static File getDiskCacheDir(Context context) {
File cacheFile;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
cacheFile = context.getExternalCacheDir();
} else {
cacheFile = context.getCacheDir();
}
return cacheFile;
}
}
package com.bolan.android.modules.update;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import com.bolan.android.modules.Utils;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
@SuppressWarnings("unused")
public class Updater extends ReactContextBaseJavaModule {
private static volatile boolean updating = false;
private static volatile boolean cancel = false;
public Updater(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "Updater";
}
@ReactMethod
public synchronized void update(final String sUrl, final Callback cb, final Promise promise) {
if (!updating) {
updating = true;
cancel = false;
new Thread(new Runnable() {
@Override
public void run() {
InputStream is = null;
FileOutputStream fos = null;
File cacheFile = Utils.getDiskCacheDir(getCurrentActivity());
File downloadFile = new File(cacheFile, "download");
File apkFile = new File(downloadFile, "bolan.apk");
try {
if (!downloadFile.exists()) {
if (!downloadFile.mkdir()) {
promise.reject("ERROR_UPDATE", "Unable to create the download directory.");
return;
}
}
File[] files = downloadFile.listFiles();
for (File file : files) {
if (!file.delete()) {
Log.w("Updater", "Unable to delete the old downloaded file: " + file.getAbsolutePath());
}
}
URL url = new URL(sUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
int length = connection.getContentLength();
is = connection.getInputStream();
fos = new FileOutputStream(apkFile);
int count = 0;
byte buf[] = new byte[1024 * 64];
do {
int numRead = is.read(buf);
count += numRead;
int progress = (int) (((float) count / length) * 100);
cb.invoke("downloading", progress, count, length);
if (numRead <= 0) {
cb.invoke("downloaded", progress, count, length);
break;
}
fos.write(buf, 0, numRead);
} while (!cancel);
if (cancel) {
return;
}
Intent i = new Intent(Intent.ACTION_VIEW);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setDataAndType(Uri.parse("file://" + apkFile.toString()), "application/vnd.android.package-archive");
Context context = getCurrentActivity();
if (context == null) {
promise.reject("ERROR_UPDATE", "Unable to get the context.");
return;
}
getCurrentActivity().startActivity(i);
android.os.Process.killProcess(android.os.Process.myPid());
promise.resolve(null);
} catch (MalformedURLException e) {
promise.reject("ERROR_UPDATE", "invalid url: " + sUrl);
} catch (Exception e) {
promise.reject(e);
} finally {
cancel = false;
updating = false;
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
Log.e("Updater", "Unable to close file stream.", e);
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
Log.e("Updater", "Unable to close url stream.", e);
}
}
}
}
});
} else {
promise.reject("ERROR_UPDATE", "There is another updater worker running already.");
}
}
public synchronized void cancel(final Promise promise) {
cancel = true;
promise.resolve(null);
}
}
......@@ -5,6 +5,7 @@ import android.content.Context;
import android.support.multidex.MultiDex;
import com.bolan.android.modules.IDCardReactPackage;
import com.bolan.android.modules.UpdaterReactPackage;
import com.facebook.react.ReactApplication;
import com.lwansbrough.RCTCamera.RCTCameraPackage;
import com.microsoft.codepush.react.CodePush;
......@@ -42,6 +43,8 @@ public class MainApplication extends Application implements ReactApplication {
new CodePush(BuildConfig.CODEPUSH_KEY, getApplicationContext(), BuildConfig.DEBUG, "http://192.168.1.2:3000"),
new RNDeviceInfo(),
new IDCardReactPackage(),
new UpdaterReactPackage()
new IDCardReactPackage(),
new VectorIconsPackage(),
new SplashScreenReactPackage()
);
......
/** @module native/Updater */
import { NativeModules } from 'react-native';
const { Updater } = NativeModules;
/**
* @callback UpdateCallback 更新回调
* @param {string} status 可能值为downloading(下载中)和downloaded(下载完成)
* @param {number} progress 下载进度,0到100的整数
* @param {number} count 已下载字节数
* @param {number} length 总字节数
*/
/**
* 下载更新包并自动安装
* @param {string} url 更新地址
* @param {UpdateCallback} cb 更新回调
* @returns {Promise.<null>}
*/
export const update = async (url, cb) => {
return Updater.update(url, cb);
};
/**
* 取消当前更新进程
* @returns {Promise.<void>}
*/
export const cancel = async () => {
return Updater.cancel();
};
/** @module services/update */
import DeviceInfo from 'react-native-device-info';
import post from '../utils/post';
import config from '../utils/config';
/**
* @typedef {Object} DeploymentInfo 部署信息
* @property {number} id 部署id
* @property {string} versionNumber 版本号
* @property {string} description 描述,一般为更新说明
* @property {Date} updateTime 部署发布时间
* @property {string} uri 资源内部uri,表示app安装包文件的内部定位地址
* @property {string} status 部署状态,可能值为release,development,broken。release表示公开版本,development表示内部测试版,broken表示有重大bug,短时间不能解决,需要回滚
*/
/**
* @typedef {Object} VersionCheck 升级检查结果
* @property {string} action 可能值分别为update(可更新),rollback(需回滚),upToDate(已经是最新版,无需更新)
* @property {DeploymentInfo} deploymentInfo 需更新或回滚的部署包信息
*/
/**
* 升级检查
* @returns {Promise.<RestResponse<VersionCheck>>}
*/
export const checkUpdate = async () => {
return post(`${config.updateContextPath}/api/app/user/apps/check`, { name: config.productId, version: DeviceInfo.getVersion() });
};
......@@ -30,8 +30,9 @@ const config = {
productId: 'manager-app-sz',
footerText: '上海铂蓝信息科技有限公司',
contextPath: '',
apiContextPath: 'http://14.21.68.149:9089/test',
// apiContextPath: 'http://192.168.1.22:8080/bm',
updateContextPath: 'http://192.168.1.22/app',
// apiContextPath: 'http://14.21.68.149:9089/test',
apiContextPath: 'http://192.168.1.22:8080/bm',
defaultDateFormat,
defaultTimeFormat,
defaultDateTimeFormat,
......
import _ from 'lodash';
import { Resolver } from 'fastjson_ref_resolver';
/**
* @global
* @typedef {Object} RestResponse
* @template T
* @property {number} errorCode 错误码,0表示成功
* @property {T} data 数据
*/
export function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
......
/** @module utils/post */
/* eslint-disable no-param-reassign */
import _ from 'lodash';
import { fetch } from './polyfill';
......@@ -13,7 +14,15 @@ const defaultOptions = {
},
};
/**
* post方法
* @param {string} url 地址
* @param {Object} data body中的数据
* @param {Object} params
* @param {Object} options
* @param {boolean} auth
* @returns {Promise.<*>}
*/
export default async function post(url, data, params = {}, options = {}, auth = true) {
if (!data) {
data = {};
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论