controller.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'dart:typed_data';
  4. import 'package:flutter/gestures.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter/services.dart';
  7. import 'package:get/get.dart';
  8. import 'package:o2oa_all_platform/common/extension/date_extension.dart';
  9. import '../../common/api/index.dart';
  10. import '../../common/models/index.dart';
  11. import '../../common/routers/index.dart';
  12. import '../../common/services/index.dart';
  13. import '../../common/utils/index.dart';
  14. import '../../common/values/index.dart';
  15. import '../../common/widgets/index.dart';
  16. import '../../environment_config.dart';
  17. import '../../global.dart';
  18. import '../common/inner_webview/index.dart';
  19. import '../home/settings/account_safe/gesture_unlock/index.dart';
  20. import '../login/server_info/index.dart';
  21. import 'state.dart';
  22. class SplashController extends GetxController {
  23. SplashController();
  24. final state = SplashState();
  25. Uint8List? launchLogo;
  26. /// 在 widget 内存中分配后立即调用。
  27. @override
  28. void onInit() {
  29. super.onInit();
  30. }
  31. /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
  32. @override
  33. void onReady() {
  34. O2UI.hideStatusBar();
  35. state.copyright =
  36. '@ ${DateTime.now().year} ${'appName'.tr} All right reserved.';
  37. _checkPrivacyAgree();
  38. super.onReady();
  39. }
  40. @override
  41. void onClose() {
  42. O2UI.showStatusBar();
  43. super.onClose();
  44. }
  45. /// 是否同意了隐私政策
  46. void _checkPrivacyAgree() {
  47. if (EnvironmentConfig.needCheckPrivacyAgree()) {
  48. final isAgree = SharedPreferenceService.to
  49. .getBool(SharedPreferenceService.isAgreePrivacyKey);
  50. if (isAgree) {
  51. _splashLoading();
  52. return;
  53. }
  54. _privacyDialog();
  55. } else {
  56. _splashLoading();
  57. }
  58. }
  59. /// 隐私政策 弹出窗口
  60. void _privacyDialog() async {
  61. final context = Get.context;
  62. if (context == null) {
  63. OLogger.i('没有 context,退出 APP');
  64. exit(0);
  65. }
  66. RichText richText = RichText(
  67. text: TextSpan(
  68. text: 'privacy_policy_dialog_content_part1'.tr,
  69. style: Theme.of(context).textTheme.bodyMedium,
  70. children: [
  71. TextSpan(
  72. text: 'user_agreement_book_mark'.tr,
  73. style: TextStyle(
  74. color: Theme.of(context).colorScheme.primary,
  75. fontSize: 14),
  76. recognizer: TapGestureRecognizer()
  77. ..onTap = () => openUserAgreement()),
  78. TextSpan(
  79. text: 'privacy_policy_dialog_content_part2'.tr,
  80. style: Theme.of(context).textTheme.bodyMedium,
  81. ),
  82. TextSpan(
  83. text: 'privacy_policy_book_mark'.tr,
  84. style: TextStyle(
  85. color: Theme.of(context).colorScheme.primary,
  86. fontSize: 14),
  87. recognizer: TapGestureRecognizer()
  88. ..onTap = () => openPrivacyPolicy()),
  89. TextSpan(
  90. text: 'privacy_policy_dialog_content_part3'.tr,
  91. style: Theme.of(context).textTheme.bodyMedium,
  92. ),
  93. ]),
  94. );
  95. final result = await O2UI.showCustomDialog(
  96. context, 'privacy_policy_dialog_title'.tr, richText,
  97. positiveBtnText: 'agree'.tr, cancelBtnText: 'disagree'.tr);
  98. if (result != null && result == O2DialogAction.positive) {
  99. _agree();
  100. _splashLoading();
  101. } else {
  102. // 退出
  103. OLogger.i('不同意,退出 APP');
  104. exit(0);
  105. }
  106. }
  107. /// 写入标记
  108. void _agree() {
  109. SharedPreferenceService.to
  110. .putBool(SharedPreferenceService.isAgreePrivacyKey, true);
  111. }
  112. void openUserAgreement() {
  113. InnerWebviewPage.open(O2.userAgreementUrl);
  114. }
  115. void openPrivacyPolicy() {
  116. InnerWebviewPage.open(O2.privacyPolicyUrl);
  117. }
  118. void _denyRoot() {
  119. final context = Get.context;
  120. if (context == null) {
  121. OLogger.i('没有 context,退出 APP');
  122. exit(0);
  123. }
  124. O2UI.showAlert(context, 'deny_root'.tr, okPressed: () => exit(0));
  125. }
  126. /// 启动加载过程
  127. Future<void> _splashLoading() async {
  128. // 安全检查
  129. final result = await O2FlutterMethodChannelUtils().checkRootNative();
  130. if (result) {
  131. _denyRoot();
  132. return;
  133. }
  134. Global.initLater(); // 需要同意后执行的
  135. // 演示版本 app
  136. if (EnvironmentConfig.isDemoApp()) {
  137. // 获取演示服务器列表
  138. await SampleEditionManger.instance.loadServerListFromWwwService();
  139. await SampleEditionManger.instance.setCurrentAndReloadApp(
  140. SampleEditionManger.instance.getCurrent(),
  141. needReload: false);
  142. } else {
  143. // 检查绑定服务器和服务器信息
  144. var hasUnitInfo = await O2ApiManager.instance.loadO2UnitFromSP();
  145. if (!hasUnitInfo) {
  146. // 没有服务器信息 去绑定
  147. OLogger.i('splash_nobind_go'.tr);
  148. state.tips = 'splash_nobind_go'.tr;
  149. Get.offNamed(O2OARoutes.bind);
  150. return;
  151. }
  152. }
  153. // 开始连接 O2OA 服务器
  154. state.tips = 'splash_connecting'.tr;
  155. // 连接服务器
  156. try {
  157. OLogger.d('开始连接O2OA服务器。。。。。');
  158. var isLoadO2OA = await ProgramCenterService.to.loadCenterServerOnline();
  159. if (!isLoadO2OA) {
  160. _connectServerFail();
  161. } else {
  162. // 移除网络监听
  163. if (Platform.isIOS) {
  164. NetworkCheckHelper().clearDelegate();
  165. }
  166. state.tips = 'splash_logining'.tr;
  167. try {
  168. await ProgramCenterService.to.loadCurrentStyle(); // 获取appstyle
  169. state.showLaunchLogo = true; // 显示 logo
  170. // 读取设备号
  171. if (GetPlatform.isMobile) {
  172. O2ApiManager.instance.getJPushDeviceIdRemote();
  173. }
  174. } catch (err, stackTrace) {
  175. OLogger.e('获取appStyle失败', err, stackTrace);
  176. }
  177. // 登录判断
  178. final isLogin = await _checkLogin();
  179. OLogger.d('登录: $isLogin');
  180. // 先显示推广页
  181. await _checkAndShowPromotionPages();
  182. if (isLogin) {
  183. _gotoHome();
  184. } else {
  185. // 清除保存的用户信息
  186. O2ApiManager.instance.cleanUser();
  187. _goToLogin();
  188. }
  189. }
  190. } catch (err, stackTrace) {
  191. OLogger.e('连接服务器失败', err, stackTrace);
  192. _connectServerFail();
  193. }
  194. }
  195. // int _iosReconnectTimes = 0;
  196. /// 显示连接错误信息
  197. void _connectServerFail() {
  198. /// ios 初次连接的是网络权限还没开 开启网络监听
  199. // if (Platform.isIOS && _iosReconnectTimes < 3) {
  200. // state.tips = 'splash_connect_fail_ios_waiting'.tr;
  201. // _iosReconnectTimes++;
  202. // OLogger.i('添加网络监听 $_iosReconnectTimes');
  203. // NetworkCheckHelper().setDelegate((result) {
  204. // OLogger.i('网络连接情况切换: $result');
  205. // _splashLoading();
  206. // });
  207. // } else {
  208. OLogger.i('splash_connect_fail'.tr);
  209. state.tips = 'splash_connect_fail'.tr;
  210. if (!EnvironmentConfig.isDirectConnectMode()) {
  211. List<DialogActionData> actions = [
  212. DialogActionData(title: 'cancel'.tr, pressedCall: () => exit(0)),
  213. DialogActionData(
  214. title: 'sample_server_name'.tr,
  215. pressedCall: () => O2ApiManager.instance.putSampleUnitServerAndReload()),
  216. DialogActionData(
  217. title: 'login_form_rebind'.tr,
  218. pressedCall: () => _rebind()),
  219. ];
  220. O2UI.showConfirmWithActions(
  221. Get.context, 'connect_o2_server_fail_go_to_sample'.tr, actions);
  222. // O2UI.showConfirm(Get.context, 'connect_o2_server_fail_go_to_sample'.tr,
  223. // okText: 'sample_server_name'.tr,
  224. // okPressed: () => O2ApiManager.instance.putSampleUnitServerAndReload(),
  225. // cancelPressed: () => exit(0));
  226. } else {
  227. // 通过重试按钮处理网络问题
  228. List<DialogActionData> actions = [
  229. DialogActionData(title: 'cancel'.tr, pressedCall: () => exit(0)),
  230. DialogActionData(
  231. title: 'connect_o2_server_fail_go_to_edit'.tr,
  232. pressedCall: () => ServerInfoPage.open(finishSelf: true)),
  233. DialogActionData(
  234. title: 'connect_o2_server_fail_refresh'.tr,
  235. pressedCall: () => _splashLoading()),
  236. ];
  237. O2UI.showConfirmWithActions(
  238. Get.context, 'connect_o2_server_fail_confirm_edit'.tr, actions);
  239. // O2UI.showConfirm(Get.context, 'connect_o2_server_fail_confirm_edit'.tr,
  240. // okText: 'connect_o2_server_fail_go_to_edit'.tr,
  241. // okPressed: () => ServerInfoPage.open(finishSelf: true),
  242. // cancelPressed: () => exit(0));
  243. }
  244. // }
  245. }
  246. /// 检测是否有登录信息 并获取用户最新登录信息
  247. Future<bool> _checkLogin() async {
  248. O2Person? userJson = O2ApiManager.instance.loadUserFromSP();
  249. if (userJson == null) {
  250. return false;
  251. } else {
  252. try {
  253. O2Person? person = await OrgAuthenticationService.to.who();
  254. if (person == null || person.token == null || person.token!.isEmpty) {
  255. return false;
  256. } else {
  257. // 更新用户信息
  258. await O2ApiManager.instance.setupUser(person);
  259. return true;
  260. }
  261. } catch (err, stackTrace) {
  262. OLogger.e('检查用户登录情况失败', err, stackTrace);
  263. return false;
  264. }
  265. }
  266. }
  267. /// 去登录
  268. void _goToLogin() {
  269. if (EnvironmentConfig.o2AppIsDemo) {
  270. Get.offNamed(O2OARoutes.demoLogin);
  271. } else {
  272. Get.offNamed(O2OARoutes.login);
  273. }
  274. }
  275. /// 去主页
  276. void _gotoHome() {
  277. final openBioAuth = SharedPreferenceService.to
  278. .getBool(SharedPreferenceService.appBiometricAuthKey);
  279. final gesturePassword = SharedPreferenceService.to
  280. .getString(SharedPreferenceService.appGestureUnlockAuthKey);
  281. if (openBioAuth) {
  282. // 人脸或指纹认证
  283. Get.offNamed(O2OARoutes.homeSettingsAccountSafeBiometricAuth);
  284. } else if (gesturePassword.isNotEmpty) {
  285. GestureUnlockPage.openGestureUnlock();
  286. } else {
  287. Get.offNamed(O2OARoutes.home);
  288. }
  289. }
  290. final Completer<void> _promotionPageCompleter = Completer<void>();
  291. /// 检查是否要打开推广页
  292. Future<void> _checkAndShowPromotionPages() async {
  293. final script = ProgramCenterService.to.promotionPageScript();
  294. OLogger.d('脚本:$script');
  295. if (script.isNotEmpty) {
  296. Map<String, dynamic> body = <String, dynamic>{};
  297. final result =
  298. await ProgramCenterService.to.executeScript(script, body);
  299. final res = SplashPromotionPageScriptResponse.fromJson(result);
  300. if (res.images != null && res.images!.isNotEmpty) { // 有推广页
  301. final frequency = res.frequency ?? '';
  302. final lasShowDate = SharedPreferenceService.to.getString(SharedPreferenceService.promotionPageShowDateKey);
  303. final today = DateTime.now().ymd();
  304. bool isShowPromotionPage = false;
  305. if (frequency == SplashPromotionPageScriptResponse.always) {
  306. isShowPromotionPage = true;
  307. } else if (frequency == SplashPromotionPageScriptResponse.daily && lasShowDate != today) { // 今天没有展现过
  308. isShowPromotionPage = true;
  309. } else if (lasShowDate.isEmpty) { // 从来没有展现过
  310. isShowPromotionPage = true;
  311. }
  312. if (isShowPromotionPage) {
  313. state.promotionPageList.addAll(res.images!);
  314. SharedPreferenceService.to.putString(SharedPreferenceService.promotionPageShowDateKey, today);
  315. await _promotionPageCompleter.future; // 等待
  316. }
  317. OLogger.d('推广页 业务完成!');
  318. }
  319. }
  320. }
  321. /// 关闭推广页
  322. void closePromotionPage() {
  323. OLogger.d('关闭推广页');
  324. _promotionPageCompleter.complete();
  325. }
  326. Future<void> _rebind () async {
  327. await O2ApiManager.instance.cleanO2UnitAndSp();
  328. _splashLoading();
  329. }
  330. }