import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:rasadyar_app/data/model/app_info_model.dart'; import 'package:rasadyar_app/infrastructure/utils/local_storage_utils.dart'; import 'package:rasadyar_core/core.dart'; class SplashLogic extends GetxController with GetTickerProviderStateMixin { late final AnimationController scaleController; late final AnimationController rotateController; Rxn> scaleAnimation = Rxn(); Rxn> rotationAnimation = Rxn(); RxBool hasUpdated = false.obs; RxBool onUpdateDownload = false.obs; RxDouble percent = 0.0.obs; final RxnString _updateFilePath = RxnString(); final platform = MethodChannel('apk_installer'); final Dio _dio = Dio(); var gService = Get.find(); var tokenService = Get.find(); AppInfoModel? appInfoModel; @override void onInit() { super.onInit(); scaleController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), ); rotateController = AnimationController( vsync: this, duration: const Duration(milliseconds: 8000), ); scaleAnimation.value = Tween( begin: 0.8, end: 1.2, ).animate(scaleController); rotationAnimation.value = Tween( begin: 0.0, end: 1, ).animate(rotateController); rotateController.forward(); rotateController.addStatusListener((status) { if (status == AnimationStatus.completed) { rotateController.repeat(); } else if (status == AnimationStatus.dismissed) { rotateController.forward(); } }); scaleController.forward(); scaleController.addStatusListener((status) { if (status == AnimationStatus.completed) { scaleController.reverse(); } else if (status == AnimationStatus.dismissed) { scaleController.forward(); } }); hasUpdated.listen((data) { if (data) { requiredUpdateDialog( onConfirm: () async { await fileDownload(); }, ); } else if (!data && Get.isDialogOpen == true) { Get.back(); } }); onUpdateDownload.listen((data) { hasUpdated.value = false; if (data) { Get.bottomSheet( isDismissible: false, isScrollControlled: true, backgroundColor: Colors.transparent, Container( height: 170, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), ), padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, spacing: 8, children: [ const Text('در حال دانلود بروزرسانی برنامه...'), Obx( () => Row( spacing: 8, children: [ Expanded( child: LinearProgressIndicator( value: percent.value, color: AppColor.greenNormal, minHeight: 4, ), ), SizedBox( width: 55.w, child: Text( '${(percent.value * 100).toStringAsFixed(2)}%', textAlign: TextAlign.center, ), ), ], ), ), Row( spacing: 8, children: [ Expanded( child: ObxValue((data) { return RElevated( backgroundColor: AppColor.greenNormal, height: 40.h, onPressed: data.value != null ? () { installApk(); Get.back(); } : null, text: 'نصب', ); }, _updateFilePath), ), Expanded( child: ROutlinedElevated( borderColor: AppColor.error, text: 'خروج', onPressed: () async { exit(0); }, ), ), ], ), ], ), ), ); } }); } @override void onReady() { super.onReady(); Future.delayed(const Duration(milliseconds: 250), () async { try { if (!kDebugMode) { final isUpdateNeeded = await checkVersion(); if (isUpdateNeeded) return; } final module = gService.getSelectedModule(); final target = gService.getTargetPage(module); if (module != null) { tokenService.setGlobalTokenAndRefToken(module); } if (target != null) { var mFuns = getFunctionsList(target.functions); await Future.wait(mFuns ?? []); Get.offAndToNamed(target.route!); } } catch (e, st) { eLog("onReady error: $e\n$st"); } }); } @override void onClose() { rotateController.dispose(); scaleController.dispose(); super.onClose(); } Future checkVersion() async { try { final info = await PackageInfo.fromPlatform(); int version = info.version.versionNumber; var res = await _dio.get( "https://rsibackend.rasadyar.com/app/rasadyar-app-info/", ); appInfoModel = AppInfoModel.fromJson(res.data); if ((appInfoModel?.info?.version?.versionNumber ?? 0) > version) { hasUpdated.value = !hasUpdated.value; return true; } return false; } catch (e) { return false; } } Future fileDownload() async { onUpdateDownload.value = true; final dir = await getApplicationDocumentsDirectory(); final filePath = "${dir.path}/rasadyar.apk"; final file = File(filePath); if (await file.exists()) { await file.delete(); } int attempts = 0; int retryCount = 4; bool success = false; while (attempts < retryCount && !success) { try { await _dio.download( appInfoModel?.file ?? '', filePath, onReceiveProgress: (count, total) { if (total != -1 && total > 0) { percent.value = count / total; } }, ); success = true; } on DioException catch (e) { attempts++; percent.value = 0.0; if (attempts >= retryCount) { eLog("Download failed after $attempts attempts: ${e.message}"); } else { await Future.delayed(const Duration(milliseconds: 1200)); } } } if (success) { _updateFilePath.value = filePath; } onUpdateDownload.value = false; } Future installApk() async { try { eLog(_updateFilePath.value); await platform.invokeMethod('apk_installer', { 'appPath': _updateFilePath.value, }); } catch (e) { eLog(e); } } }