@@ -10,12 +10,12 @@ class UserInfoModel { | |||
String username; | |||
List<String> perms; | |||
String registerInviteCodeEnable; | |||
bool isTBAuth; | |||
bool isTBAuth; // 是否淘宝授权 | |||
// 获取模糊手机号码 | |||
get blurMobile => !EmptyUtil.isEmpty(mobile) ? mobile.length == 11 ? '${mobile.substring(0, 3)}****${mobile.substring(7, mobile.length)}' : mobile : mobile; | |||
UserInfoModel({this.token, this.userId, this.username, this.perms, this.registerInviteCodeEnable}); | |||
UserInfoModel({this.token, this.userId, this.username, this.perms, this.registerInviteCodeEnable, this.isTBAuth = false}); | |||
UserInfoModel.fromJson(Map<String, dynamic> json) { | |||
token = json['token']; | |||
@@ -28,15 +28,25 @@ class UserInfoNotifier with ChangeNotifier { | |||
} | |||
return _userInfo; | |||
} | |||
/// 更新淘宝授权 | |||
void updateUserAuth(bool isAuth){ | |||
if(_userInfo != null){ | |||
void updateUserAuth(bool isAuth) { | |||
if (null == _userInfo) { | |||
getUserInfoModel().then((user) { | |||
if (user != null) { | |||
_userInfo = user; | |||
_userInfo.isTBAuth = isAuth; | |||
Logger.log('upate data TBAuth'); | |||
notifyListeners(); | |||
} | |||
}); | |||
} else { | |||
_userInfo.isTBAuth = isAuth; | |||
Logger.log('upate data TBAuth'); | |||
notifyListeners(); | |||
} | |||
} | |||
/// 更新用户数据 | |||
void setUserInfo(UserInfoModel loginUser) async { | |||
print('${loginUser.toString()}'); | |||
@@ -73,7 +83,7 @@ class UserInfoNotifier with ChangeNotifier { | |||
String userInfoJson = await SharedPreferencesUtil.getStringValue( | |||
GlobalConfig.SHARED_KEY_USER_INFO); | |||
if (userInfoJson == null || userInfoJson == '') { | |||
_userInfo = UserInfoModel(); | |||
_userInfo = UserInfoModel(); | |||
} else { | |||
_userInfo = UserInfoModel.fromJson(jsonDecode(userInfoJson)); | |||
} | |||
@@ -1,3 +1,4 @@ | |||
import 'dart:collection'; | |||
import 'dart:convert'; | |||
import 'dart:io'; | |||
import 'dart:ui'; | |||
@@ -77,53 +78,46 @@ class NetUtil { | |||
/// 设置代理 | |||
void _setProxy(String proxyUrl) { | |||
(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = | |||
(HttpClient client) { | |||
(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (HttpClient client) { | |||
client.findProxy = (uri) { | |||
return "PROXY $proxyUrl"; | |||
}; | |||
client.badCertificateCallback = | |||
(X509Certificate cert, String host, int port) => true; | |||
client.badCertificateCallback = (X509Certificate cert, String host, int port) => true; | |||
}; | |||
} | |||
/// 同步请求 | |||
static Future<dynamic> post(String path, | |||
{Map<String, dynamic> params, | |||
NetMethod method = NetMethod.POST, | |||
bool cache = false}) async { | |||
static Future<dynamic> post(String path, {Map<String, dynamic> params, NetMethod method = NetMethod.POST, bool cache = false}) async { | |||
if (params == null) { | |||
params = {}; | |||
} | |||
// 根据请求参数,获取缓存的Key | |||
String cacheKey = getRequestParamsCachedKey(params, path); | |||
// 参数签名 TODO 加密? | |||
Map<String, dynamic> sign = await signParams(params); | |||
// post请求的参数 | |||
Map<String, dynamic> bodyParams = params; | |||
// 请求头参数 | |||
Map<String, dynamic> headParam = await _getMustHeadParams(); | |||
Response response; | |||
try { | |||
String token = await SharedPreferencesUtil.getStringValue( | |||
GlobalConfig.SHARED_KEY_TOKEN) ?? | |||
''; | |||
Dio dio = await NetUtil.getInstance().dio; | |||
response = await dio.request(path, | |||
data: sign, | |||
options: Options(method: enumToString(method), headers: {'token': token, 'app_version': sign['app_version']}), | |||
// options: Options(method: enumToString(method), headers: sign), | |||
response = await dio.request( | |||
path, | |||
data: !EmptyUtil.isEmpty(bodyParams) ? bodyParams : null, | |||
options: Options(method: enumToString(method), headers: headParam), | |||
); | |||
} on DioError catch (e) { | |||
_formatError(e); | |||
} | |||
try { | |||
var result = | |||
response.data is Map ? response.data : jsonDecode(response.data); | |||
var result = response.data is Map ? response.data : jsonDecode(response.data); | |||
// TODO 解密? | |||
if (isSuccess(result)) { | |||
// 缓存成功的数据 | |||
if (cache) { | |||
_setCallBackCacheData(cacheKey, | |||
response.data is Map ? jsonEncode(response.data) : response.data); | |||
_setCallBackCacheData(cacheKey, response.data is Map ? jsonEncode(response.data) : response.data); | |||
} | |||
return result; | |||
// 缓存返回的数据 | |||
@@ -145,12 +139,7 @@ class NetUtil { | |||
} | |||
/// 异步请求 | |||
static void request(String path, | |||
{NetMethod method = NetMethod.GET, | |||
Map<String, dynamic> params, | |||
OnSuccess onSuccess, | |||
OnError onError, | |||
OnCache onCache}) async { | |||
static void request(String path, {NetMethod method = NetMethod.GET, Map<String, dynamic> params, OnSuccess onSuccess, OnError onError, OnCache onCache}) async { | |||
if (params == null) { | |||
params = {}; | |||
} | |||
@@ -173,11 +162,7 @@ class NetUtil { | |||
} | |||
if (onError != null) { | |||
onError(!EmptyUtil.isEmpty(result) | |||
? !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]) | |||
? result[GlobalConfig.HTTP_RESPONSE_KEY_MSG] | |||
: '未知错误' | |||
: '未知错误'); | |||
onError(!EmptyUtil.isEmpty(result) ? !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]) ? result[GlobalConfig.HTTP_RESPONSE_KEY_MSG] : '未知错误' : '未知错误'); | |||
} | |||
} catch (e) { | |||
Logger.error('error: ' + e.toString()); | |||
@@ -190,8 +175,85 @@ class NetUtil { | |||
} | |||
/// 统一添加必要的参数 | |||
static Future<Map<String, dynamic>> signParams( | |||
Map<String, dynamic> params) async { | |||
// static Future<Map<String, dynamic>> signParams(Map<String, dynamic> params) async { | |||
// // 应用信息 | |||
// PackageInfo packageInfo = await PackageInfo.fromPlatform(); | |||
// // 原生传的信息 | |||
// Map setting = await NativeUtil.getSetting(); | |||
// | |||
// if (Platform.isIOS) { | |||
// IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo; | |||
// // 设备 | |||
// params["platform"] = "ios"; | |||
// // 设备系统版本 | |||
// params["system_version"] = iosInfo?.systemVersion; | |||
// // 设备型号 如:iPhone 11pro | |||
// params['device_model'] = iosInfo?.name; | |||
// // 设备型号,如:MLMF2LL/A | |||
// // params['device_number'] = iosInfo?.model; | |||
// // 设备ID | |||
// params['device_id'] = iosInfo?.identifierForVendor; | |||
// } else if (Platform.isAndroid) { | |||
// AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; | |||
// // 设备 | |||
// params["platform"] = "android"; | |||
// // 设备系统版本 | |||
// params["system_version"] = "Android ${androidInfo?.version?.release}"; | |||
// // 设备型号 如:iPhone 11pro | |||
// params['device_model'] = androidInfo?.model; | |||
// // 设备型号,如:MLMF2LL/A | |||
// // params['device_number'] = androidInfo?.id; | |||
// // 设备Id | |||
// params['device_id'] = androidInfo?.androidId; | |||
// } | |||
// // 应用版本号 | |||
// params["app_version_name"] = packageInfo.version; | |||
// params["app_version"] = -1 ;// packageInfo.buildNumber; | |||
// // 分辨率 | |||
// params["solution"] = | |||
// "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}"; | |||
// | |||
// // 站长ID | |||
// String masterId = setting['master_id']; | |||
// if (null != masterId && | |||
// masterId != '' && | |||
// (!params.containsKey('master_id') || params['master_id'] == '')) { | |||
// params['master_id'] = masterId; | |||
// } | |||
// | |||
// // secret_key | |||
// params['secret_key'] = setting['secret_key']; | |||
// | |||
// // token 读取SP缓存中的用户token | |||
// String token = await SharedPreferencesUtil.getStringValue( | |||
// GlobalConfig.SHARED_KEY_TOKEN); | |||
// if (!EmptyUtil.isEmpty(token)) { | |||
// params['token'] = token; | |||
// } | |||
// | |||
// // 当前时间戳:秒 | |||
// params["time"] = TimeUtil.getNowTime(); | |||
// | |||
// // 过滤空字段,过滤首尾空格 | |||
// Map<String, dynamic> filters = Map<String, dynamic>(); | |||
// params.forEach((key, value) { | |||
// if (key != '' && value != '') { | |||
// filters[key] = (value is String) ? value.trim() : value; | |||
// } | |||
// }); | |||
// params = filters; | |||
// | |||
// List<String> list = List<String>(); | |||
// params.forEach((key, value) { | |||
// list.add(key.toString() + value.toString()); | |||
// }); | |||
// params["sign"] = signWithArray(list); | |||
// return params; | |||
// } | |||
/// 获取必须的请求参数(用于请求头部) | |||
static Future<Map<String, String>> _getMustHeadParams() async { | |||
Map<String, String> params = new HashMap<String, String>(); | |||
// 应用信息 | |||
PackageInfo packageInfo = await PackageInfo.fromPlatform(); | |||
// 原生传的信息 | |||
@@ -202,56 +264,47 @@ class NetUtil { | |||
// 设备 | |||
params["platform"] = "ios"; | |||
// 设备系统版本 | |||
params["system_version"] = iosInfo?.systemVersion; | |||
params["os_version"] = iosInfo?.systemVersion?.toString(); | |||
// 设备型号 如:iPhone 11pro | |||
params['device_model'] = iosInfo?.name; | |||
// 设备型号,如:MLMF2LL/A | |||
// params['device_number'] = iosInfo?.model; | |||
params['device_model'] = iosInfo?.name?.toString(); | |||
// 设备ID | |||
params['device_id'] = iosInfo?.identifierForVendor; | |||
params['device_id'] = iosInfo?.identifierForVendor?.toString(); | |||
} else if (Platform.isAndroid) { | |||
AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; | |||
// 设备 | |||
params["platform"] = "android"; | |||
// 设备系统版本 | |||
params["system_version"] = "Android ${androidInfo?.version?.release}"; | |||
params["os_version"] = "Android ${androidInfo?.version?.release}"; | |||
// 设备型号 如:iPhone 11pro | |||
params['device_model'] = androidInfo?.model; | |||
// 设备型号,如:MLMF2LL/A | |||
// params['device_number'] = androidInfo?.id; | |||
params['device_model'] = androidInfo?.model?.toString(); | |||
// 设备Id | |||
params['device_id'] = androidInfo?.androidId; | |||
params['device_id'] = androidInfo?.androidId?.toString(); | |||
} | |||
// 应用版本号 | |||
params["app_version_name"] = packageInfo.version; | |||
params["app_version"] = packageInfo.buildNumber; | |||
params["app_version_name"] = packageInfo.version?.toString(); | |||
params["app_version"] = packageInfo.buildNumber?.toString(); | |||
// 分辨率 | |||
params["solution"] = | |||
"${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}"; | |||
params["solution"] = "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}"; | |||
// 站长ID | |||
String masterId = setting['master_id']; | |||
if (null != masterId && | |||
masterId != '' && | |||
(!params.containsKey('master_id') || params['master_id'] == '')) { | |||
params['master_id'] = masterId; | |||
if (null != masterId && masterId != '' && (!params.containsKey('master_id') || params['master_id'] == '')) { | |||
params['master_id'] = masterId ?? 'template_database'; | |||
} | |||
// secret_key | |||
params['secret_key'] = setting['secret_key']; | |||
// token 读取SP缓存中的用户token | |||
String token = await SharedPreferencesUtil.getStringValue( | |||
GlobalConfig.SHARED_KEY_TOKEN); | |||
String token = await SharedPreferencesUtil.getStringValue(GlobalConfig.SHARED_KEY_TOKEN); | |||
if (!EmptyUtil.isEmpty(token)) { | |||
params['token'] = token; | |||
} | |||
// secret_key | |||
params['secret_key'] = setting['secret_key'] ?? ''; | |||
// 当前时间戳:秒 | |||
params["time"] = TimeUtil.getNowTime(); | |||
// 过滤空字段,过滤首尾空格 | |||
Map<String, dynamic> filters = Map<String, dynamic>(); | |||
Map<String, String> filters = Map<String, String>(); | |||
params.forEach((key, value) { | |||
if (key != '' && value != '') { | |||
filters[key] = (value is String) ? value.trim() : value; | |||
@@ -261,25 +314,23 @@ class NetUtil { | |||
List<String> list = List<String>(); | |||
params.forEach((key, value) { | |||
list.add(key.toString() + value.toString()); | |||
list.add(key.toString() + '=' + value.toString() + '&'); | |||
}); | |||
params["sign"] = signWithArray(list); | |||
params.remove('secret_key'); | |||
return params; | |||
} | |||
/// 获取请求缓存成功的数据 | |||
static Future<void> _onCallBackCacheData( | |||
OnCache onCache, String cacheKey) async { | |||
static Future<void> _onCallBackCacheData(OnCache onCache, String cacheKey) async { | |||
// 读取缓存 | |||
Map<String, dynamic> cacheMap = | |||
await SharedPreferencesUtil.getNetCacheResult(cacheKey); | |||
Map<String, dynamic> cacheMap = await SharedPreferencesUtil.getNetCacheResult(cacheKey); | |||
if (!EmptyUtil.isEmpty(cacheMap) && | |||
cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) && | |||
(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == | |||
GlobalConfig.RESPONSE_SUCCESS_CODE || | |||
cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == | |||
'${GlobalConfig.RESPONSE_SUCCESS_CODE}') && | |||
(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE || | |||
cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}') && | |||
!EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||
onCache(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||
return; | |||
@@ -293,17 +344,12 @@ class NetUtil { | |||
} | |||
/// 根据请求参数,获取缓存的数据 | |||
static Future<dynamic> getRequestCachedData(String url, | |||
{Map<String, dynamic> params}) async { | |||
Map<String, dynamic> cacheMap = | |||
await SharedPreferencesUtil.getNetCacheResult( | |||
getRequestParamsCachedKey(params, url)); | |||
static Future<dynamic> getRequestCachedData(String url, {Map<String, dynamic> params}) async { | |||
Map<String, dynamic> cacheMap = await SharedPreferencesUtil.getNetCacheResult(getRequestParamsCachedKey(params, url)); | |||
if (!EmptyUtil.isEmpty(cacheMap) && | |||
cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) && | |||
(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == | |||
GlobalConfig.RESPONSE_SUCCESS_CODE || | |||
cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == | |||
'${GlobalConfig.RESPONSE_SUCCESS_CODE}') && | |||
(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE || | |||
cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}') && | |||
!EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||
return cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]; | |||
} | |||
@@ -314,10 +360,8 @@ class NetUtil { | |||
static bool isSuccess(Map<String, dynamic> data) { | |||
try { | |||
if (!EmptyUtil.isEmpty(data) && | |||
(data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == | |||
GlobalConfig.RESPONSE_SUCCESS_CODE || | |||
data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == | |||
'${GlobalConfig.RESPONSE_SUCCESS_CODE}')) { | |||
(data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE || | |||
data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}')) { | |||
return true; | |||
} | |||
} catch (e) { | |||
@@ -327,8 +371,7 @@ class NetUtil { | |||
} | |||
/// 根据请求参数,获取缓存的Key | |||
static String getRequestParamsCachedKey( | |||
Map<String, dynamic> map, String path) { | |||
static String getRequestParamsCachedKey(Map<String, dynamic> map, String path) { | |||
if (EmptyUtil.isEmpty(map)) { | |||
return EncodeUtil.generateMd5(path); | |||
} | |||
@@ -336,11 +379,7 @@ class NetUtil { | |||
} | |||
// 七牛云文件上传 | |||
static Future uploadFile(String url, File file, | |||
{String method = 'POST', | |||
Map<String, dynamic> params, | |||
OnSuccess onSuccess, | |||
OnError onError}) async { | |||
static Future uploadFile(String url, File file, {String method = 'POST', Map<String, dynamic> params, OnSuccess onSuccess, OnError onError}) async { | |||
if (params == null) { | |||
params = {}; | |||
} | |||
@@ -362,11 +401,10 @@ class NetUtil { | |||
// 字母升序 | |||
params.sort(); | |||
String result = ""; | |||
// result += secret_key; | |||
params.forEach((param) { | |||
result += param; | |||
}); | |||
// result += secret_key; | |||
result = result.substring(0, result.length - 1); | |||
return EncodeUtil.generateMd5(result); | |||
} | |||
@@ -405,8 +443,7 @@ class _NetInterceptors extends InterceptorsWrapper { | |||
@override | |||
Future onResponse(Response response) { | |||
Logger.endNet(response?.request?.uri?.toString(), | |||
data: response?.data?.toString() ?? ''); | |||
Logger.endNet(response?.request?.uri?.toString(), data: response?.data?.toString() ?? ''); | |||
// TODO 解密? | |||
return super.onResponse(response); | |||
} | |||
@@ -10,13 +10,14 @@ import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:provider/provider.dart'; | |||
class TaobaoAuth { | |||
static ProfileModel _profile; | |||
// static ProfileModel _profile; | |||
// 淘宝授权 | |||
static auth(BuildContext context) async { | |||
bool isAuth = await TaobaoAuth.isAuth(); | |||
if (isAuth) { | |||
Fluttertoast.showToast(msg: '你已经授权过了'); | |||
Provider.of<UserInfoNotifier>(context, listen: false).updateUserAuth(true); | |||
return; | |||
} | |||
@@ -26,8 +27,7 @@ class TaobaoAuth { | |||
return TaobaoAuthAlert(); | |||
}); | |||
if (isConfirm != null && isConfirm == true) { | |||
Map<String, dynamic> data = Map<String, dynamic>.from( | |||
await NetUtil.post('/api/v1/taobao/auth', method: NetMethod.GET)); | |||
Map<String, dynamic> data = Map<String, dynamic>.from(await NetUtil.post('/api/v1/taobao/auth', method: NetMethod.GET)); | |||
Logger.debug(data); | |||
if (data['code'] != 1) { | |||
return; | |||
@@ -36,20 +36,15 @@ class TaobaoAuth { | |||
print("授权链接" + url); | |||
TradeResult result; | |||
if (Platform.isAndroid) { | |||
result = await FlutterAlibc.openByUrl( | |||
url: url, backUrl: "alisdk://", isAuth: true); | |||
// TradeResult result = await FlutterAlibc.openByUrl(url: ''); | |||
Logger.debug('${result.errorCode} ${result.errorMessage} '); | |||
result = await FlutterAlibc.openByUrl(url: url, backUrl: "alisdk://", isAuth: true); | |||
} else if (Platform.isIOS) { | |||
result = await FlutterAlibc.openByUrl(url: url); | |||
// TradeResult result = await FlutterAlibc.openByUrl(url: ''); | |||
Logger.debug('${result.errorCode} ${result.errorMessage} '); | |||
} | |||
if(null !=result && result.errorCode == '0'){ | |||
Provider.of<UserInfoNotifier>(context).updateUserAuth(true); | |||
} | |||
Logger.log('${result.errorCode} ${result.errorMessage} '); | |||
// if(null != result && result.errorCode == '0'){ | |||
// Provider.of<UserInfoNotifier>(context, listen: false).updateUserAuth(true); | |||
// } | |||
await initAuth(context); | |||
} | |||
} | |||
@@ -58,12 +53,32 @@ class TaobaoAuth { | |||
// if (_profile != null) { | |||
// return _profile.isAuth; | |||
// } | |||
Map<String, dynamic> data = Map<String, dynamic>.from( | |||
await NetUtil.post('/api/v1/user/profile', method: NetMethod.GET)); | |||
if (data['code'] == 1) { | |||
_profile = ProfileModel.fromJson(Map<String, dynamic>.from(data['data'])); | |||
return _profile.isAuth; | |||
try { | |||
Map<String, dynamic> data = Map<String, dynamic>.from(await NetUtil.post('/api/v1/user/profile', method: NetMethod.GET)); | |||
if (data['code'] == 1) { | |||
ProfileModel _profile = ProfileModel.fromJson(Map<String, dynamic>.from(data['data'])); | |||
return _profile.isAuth; | |||
} | |||
}catch(e, s){ | |||
Logger.error(e, s); | |||
} | |||
return false; | |||
} | |||
// 返回是否授权 | |||
static void initAuth(BuildContext context) async { | |||
try { | |||
Map<String, dynamic> data = Map<String, dynamic>.from(await NetUtil.post('/api/v1/user/profile', method: NetMethod.GET)); | |||
if (data['code'] == 1) { | |||
ProfileModel model = ProfileModel.fromJson(Map<String, dynamic>.from(data['data'])); | |||
if(null != model && model.isAuth){ | |||
Provider.of<UserInfoNotifier>(context, listen: false).updateUserAuth(true); | |||
} | |||
} | |||
}catch(e, s){ | |||
Logger.error(e, s); | |||
} | |||
} | |||
} |
@@ -1,11 +1,32 @@ | |||
class AppUpdateModel { | |||
/// Android的第三方URL更新链接 例如:应用宝 | |||
String appDownloadThirdpartyUrl; | |||
/// 更新的下载链接(Android为应用内的下载链接,IOS为商店链接) | |||
String appDownloadUrl; | |||
/// 新版本号码 | |||
String appVersion; | |||
/// 新版本昵称 | |||
String appVersionName; | |||
/// 更新内容 | |||
String content; | |||
/// 是否显示提示更新弹窗 | |||
String dialog; | |||
/// 是否强制更新 | |||
String isForce; | |||
/// 是否可以忽略此版本 | |||
String isIgnore; | |||
/// 是否优先使用第三方更新 | |||
String isThirdparty; | |||
/// 提示 | |||
String tip; | |||
AppUpdateModel({ | |||
@@ -17,6 +38,7 @@ class AppUpdateModel { | |||
this.isForce, | |||
this.isThirdparty, | |||
this.tip, | |||
this.content, | |||
}); | |||
AppUpdateModel.fromJson(Map<String, dynamic> json) { | |||
@@ -26,8 +48,10 @@ class AppUpdateModel { | |||
appVersionName = json['app_version_name']; | |||
dialog = json['dialog']; | |||
isForce = json['is_force']; | |||
isIgnore = json['is_ignore']; | |||
isThirdparty = json['is_thirdparty']; | |||
tip = json['tip']; | |||
content = json['content']; | |||
} | |||
Map<String, dynamic> toJson() { | |||
@@ -38,8 +62,10 @@ class AppUpdateModel { | |||
data['app_version_name'] = this.appVersionName; | |||
data['dialog'] = this.dialog; | |||
data['is_force'] = this.isForce; | |||
data['is_ignore'] = this.isIgnore; | |||
data['is_thirdparty'] = this.isThirdparty; | |||
data['tip'] = this.tip; | |||
data['content'] = this.content; | |||
return data; | |||
} | |||
} |
@@ -3,8 +3,10 @@ import 'dart:io'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:flutter_update_dialog/flutter_update_dialog.dart'; | |||
// import 'package:flutter_xupdate/flutter_xupdate.dart'; | |||
import 'package:flutter_xupdate/flutter_xupdate.dart'; | |||
import 'package:package_info/package_info.dart'; | |||
import 'package:url_launcher/url_launcher.dart'; | |||
import 'package:zhiying_comm/util/shared_prefe_util.dart'; | |||
import 'package:zhiying_comm/util/update/app_update_model.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:fluttertoast/fluttertoast.dart'; | |||
@@ -15,48 +17,49 @@ import '../log/let_log.dart'; | |||
/// App更新工具类 | |||
/// | |||
class AppUpdateUtil { | |||
static final String _IGNORE_VERSION = "xupdate_ignore_version"; | |||
/// | |||
/// 初始化更新插件 | |||
/// | |||
static void initXUpdate() { | |||
if (Platform.isAndroid) { | |||
// FlutterXUpdate.init( | |||
// | |||
// ///是否输出日志 | |||
// debug: true, | |||
// | |||
// ///是否使用post请求 | |||
// isPost: false, | |||
// | |||
// ///post请求是否是上传json | |||
// isPostJson: false, | |||
// | |||
// ///是否开启自动模式 | |||
// isWifiOnly: false, | |||
// | |||
// ///是否开启自动模式 | |||
// isAutoMode: false, | |||
// | |||
// ///需要设置的公共参数 | |||
// supportSilentInstall: false, | |||
// | |||
// ///在下载过程中,如果点击了取消的话,是否弹出切换下载方式的重试提示弹窗 | |||
// enableRetry: false) | |||
// .then((value) { | |||
// // updateMessage("初始化成功: $value"); | |||
// Logger.log('初始化成功: $value'); | |||
// }).catchError((error) { | |||
// print(error); | |||
// }); | |||
// FlutterXUpdate.setErrorHandler(onUpdateError: (Map<String, dynamic> message) async { | |||
// Logger.warn(message); | |||
// // setState(() { | |||
// // _message = "$message"; | |||
// // }); | |||
// }); | |||
FlutterXUpdate.init( | |||
///是否输出日志 | |||
debug: true, | |||
///是否使用post请求 | |||
isPost: false, | |||
///post请求是否是上传json | |||
isPostJson: false, | |||
///是否开启自动模式 | |||
isWifiOnly: false, | |||
///是否开启自动模式 | |||
isAutoMode: false, | |||
///需要设置的公共参数 | |||
supportSilentInstall: false, | |||
///在下载过程中,如果点击了取消的话,是否弹出切换下载方式的重试提示弹窗 | |||
enableRetry: false) | |||
.then((value) { | |||
Logger.log('初始化成功: $value'); | |||
}).catchError((error) { | |||
print(error); | |||
}); | |||
FlutterXUpdate.setErrorHandler(onUpdateError: (Map<String, dynamic> message) async { | |||
Logger.warn(message); | |||
}); | |||
FlutterXUpdate.setErrorHandler(onUpdateError: (Map<String, dynamic> message) async { | |||
Logger.warn(message); | |||
}); | |||
} else { | |||
// updateMessage("ios暂不支持XUpdate更新"); | |||
Logger.log('ios暂不支持XUpdate更新'); | |||
} | |||
} | |||
@@ -64,74 +67,89 @@ class AppUpdateUtil { | |||
/// | |||
/// 检查并且更新app | |||
/// | |||
static void updateApp(BuildContext context) async { | |||
PackageInfo packageInfo = await PackageInfo.fromPlatform(); | |||
Logger.log('version = ${packageInfo.version}, buildNum = ${packageInfo.buildNumber}'); | |||
var result = await NetUtil.post('/api/v1/appcheck', params: {'app_version': packageInfo.buildNumber}, method: NetMethod.GET); | |||
customStyle(context, onUpdate: (){ | |||
Navigator.pop(context); | |||
}); | |||
// UpdateEntity updateEntity = await _checkAppUpdate(); | |||
// // 有新版本,进行更新 | |||
// if (!EmptyUtil.isEmpty(updateEntity) && updateEntity.hasUpdate) { | |||
// if (Platform.isAndroid) { | |||
// FlutterXUpdate.updateByInfo( | |||
// updateEntity: updateEntity, | |||
// supportBackgroundUpdate: true, | |||
// ); | |||
// } else if (Platform.isIOS) { | |||
// // TODO 完成IOS更新 | |||
// String updateUrl = updateEntity.downloadUrl; | |||
// customStyle(context, onUpdate: (){ | |||
// | |||
// | |||
// | |||
// Navigator.pop(context); | |||
// }); | |||
// } | |||
// } else { | |||
// Fluttertoast.showToast(msg: '已经是最新版本了~'); | |||
// } | |||
/// @context 上下文 | |||
/// @needToast 是否需要提示最新版本 | |||
/// @mustShowDialog 是否必须弹窗(检查更新页面要弹窗) | |||
/// | |||
static void updateApp(BuildContext context, {bool needToast = false, bool mustShowDialog = false}) async { | |||
UpdateEntity updateEntity = await _checkAppUpdate(); | |||
// 有新版本,进行更新 | |||
if (!EmptyUtil.isEmpty(updateEntity) && updateEntity.hasUpdate) { | |||
if (Platform.isAndroid) { | |||
FlutterXUpdate.updateByInfo( | |||
updateEntity: updateEntity, | |||
supportBackgroundUpdate: true, | |||
enableIgnore: mustShowDialog || updateEntity.isForce, | |||
retryContent: '更新下载速度太慢了,是否考虑切换下载方式?', | |||
enableRetry: updateEntity.thirdParty, | |||
overrideGlobalRetryStrategy: true, | |||
retryUrl: updateEntity.thirdUrl, | |||
); | |||
} else if (Platform.isIOS) { | |||
bool ignore = await isIgnoreVersion(updateEntity?.versionCode?.toString()); | |||
if ((!ignore || mustShowDialog) || updateEntity.isForce || !updateEntity.isIgnorable) { | |||
String updateUrl = updateEntity.downloadUrl; | |||
customStyle(context, updateEntity, onUpdate: () { | |||
// 打开第三方sdk | |||
launch(updateUrl, forceSafariVC: true, enableJavaScript: true, forceWebView: true); | |||
Navigator.pop(context); | |||
}); | |||
} | |||
} | |||
} else { | |||
if (needToast) Fluttertoast.showToast(msg: '已经是最新版本了~'); | |||
} | |||
} | |||
/// 检查是否有新版本 | |||
// static Future<UpdateEntity> _checkAppUpdate() async { | |||
// try { | |||
// PackageInfo packageInfo = await PackageInfo.fromPlatform(); | |||
// // var result = await NetUtil.post('/api/v1/appcheck', params: {'app_version': packageInfo.version}, method: NetMethod.GET); | |||
// | |||
// var result = { | |||
// 'dialog': '1', | |||
// 'is_force': '0', | |||
// 'app_version': '5', | |||
// 'app_version_name': '5.0.0', | |||
// 'tip': '\n1、优化api接口。\n2、添加使用demo演示。\n3、新增自定义更新服务API接口。\n4、优化更新提示界面。', | |||
// 'app_download_url': 'http://imtt.dd.qq.com/16891/apk/FAD58DEFE56A938D86B123DEA4E298EB.apk?fsname=com.hairuyi.www_5.8.8_469.apk&hsr=4d5s', | |||
// }; | |||
// AppUpdateModel model = AppUpdateModel.fromJson(result); | |||
// if (!EmptyUtil.isEmpty(model)) { | |||
// // ⚠️ isIgnorable 目前只有两种状态,为强制更新和不强制更新(每次进来都会提示更新UI弹窗) | |||
// return UpdateEntity( | |||
// hasUpdate: model?.dialog == '1', | |||
// isIgnorable: false, | |||
// isForce: model?.isForce == '1', | |||
// versionCode: int.parse(model?.appVersion ?? '1'), | |||
// versionName: model?.appVersionName, | |||
// downloadUrl: model?.appDownLoadUrl, | |||
// updateContent: model?.tip, | |||
// ); | |||
// } | |||
// } catch (e, s) { | |||
// Logger.error(e, s); | |||
// } | |||
// return null; | |||
// } | |||
static void customStyle(BuildContext context, {@required VoidCallback onUpdate }) { | |||
static Future<UpdateEntity> _checkAppUpdate() async { | |||
try { | |||
// var result = { | |||
// "code": 1, | |||
// "data": { | |||
// "app_download_thirdparty_url": "https://android.myapp.com/myapp/detail.htm?apkName=com.hairuyi.www&ADTAG=mobile", | |||
// "app_download_url": "http://fir.osant.cn/zhiyi", | |||
// "app_version": "1", | |||
// "app_version_name": "1.0.0", | |||
// "content": "更新接口\n完善app页面\n更新站长后台", | |||
// "dialog": "1", | |||
// "is_force": "0", | |||
// "is_ignore": "0", | |||
// "is_thirdparty": "1", | |||
// "tip": "有新版下载" | |||
// }, | |||
// "msg": "ok" | |||
// }; | |||
PackageInfo packageInfo = await PackageInfo.fromPlatform(); | |||
var result = await NetUtil.post('/api/v1/appcheck', method: NetMethod.GET); | |||
AppUpdateModel model = AppUpdateModel.fromJson(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||
if (!EmptyUtil.isEmpty(model)) { | |||
return UpdateEntity( | |||
hasUpdate: model?.dialog == '1' || model?.isForce == '1', | |||
isIgnorable: model?.isIgnore == '1' && model?.isForce != '1', | |||
isForce: model?.isForce == '1', | |||
versionCode: int.parse(model?.appVersion ?? '1'), | |||
versionName: model?.appVersionName, | |||
downloadUrl: model?.appDownloadUrl, | |||
updateContent: model?.content, | |||
tip: model?.tip, | |||
thirdUrl: model?.appDownloadThirdpartyUrl, | |||
thirdParty: model?.isThirdparty == '1', | |||
); | |||
} | |||
} catch (e, s) { | |||
Logger.error(e, s); | |||
} | |||
return null; | |||
} | |||
// IOS 样式 | |||
static void customStyle(BuildContext context, UpdateEntity updateEntity, {@required VoidCallback onUpdate}) { | |||
UpdateDialog.showUpdate(context, | |||
width: 250, | |||
title: "是否升级到4.1.4版本?", | |||
updateContent: "新版本大小:2.0M\n1.xxxxxxx\n2.xxxxxxx\n3.xxxxxxx", | |||
title: "是否升级到${updateEntity?.versionName ?? '1.0.0'}版本?", | |||
updateContent: updateEntity?.updateContent ?? '', | |||
titleTextSize: 14, | |||
contentTextSize: 12, | |||
buttonTextSize: 12, | |||
@@ -140,11 +158,28 @@ class AppUpdateUtil { | |||
radius: 8, | |||
themeColor: Colors.redAccent, | |||
progressBackgroundColor: Color(0x5AFFAC5D), | |||
isForce: false, | |||
isForce: updateEntity?.isForce ?? false, | |||
updateButtonText: '升级', | |||
ignoreButtonText: '忽略此版本', | |||
enableIgnore: true, onIgnore: () { | |||
enableIgnore: updateEntity?.isIgnorable ?? false, onIgnore: () { | |||
saveIgnoreVersion(updateEntity?.versionCode?.toString()); | |||
Navigator.pop(context); | |||
}, onUpdate: onUpdate); | |||
} | |||
// 保存忽略的版本号码 | |||
static void saveIgnoreVersion(String newVersion) async { | |||
if (!EmptyUtil.isEmpty(newVersion)) { | |||
await SharedPreferencesUtil.setStringValue(_IGNORE_VERSION, newVersion); | |||
} | |||
} | |||
// 是否是忽略版本 | |||
static Future<bool> isIgnoreVersion(String newVersion) async { | |||
String oldVersion = await SharedPreferencesUtil.getStringValue(_IGNORE_VERSION); | |||
if (!EmptyUtil.isEmpty(oldVersion) && !EmptyUtil.isEmpty(newVersion)) { | |||
return oldVersion == newVersion; | |||
} | |||
return false; | |||
} | |||
} |
@@ -33,7 +33,10 @@ dependencies: | |||
# 京东sdk | |||
jdsdk: ^0.0.1 | |||
# Android app更新 | |||
# flutter_xupdate: ^1.0.0 | |||
flutter_xupdate: | |||
git: | |||
ref: 1.0.1 | |||
url: http://192.168.0.138:3000/FnuoOS_ZhiYing/flutter_xupdate.git | |||
# app更新dialogUI(用于IOS,以便统一样式) | |||
flutter_update_dialog: 1.0.0 | |||
flutter_alibc: | |||