feat : apk installer

This commit is contained in:
2025-07-19 15:35:07 +03:30
parent 69945a29cf
commit 3683e8a9e6
15 changed files with 641 additions and 79 deletions

View File

@@ -1,23 +1,18 @@
import 'package:rasadyar_auth/auth.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_core/core.dart';
final di = GetIt.instance;
Future<void> setupPreInjection() async{
Future<void> setupPreInjection() async {
await setupAllCoreProvider();
await setupAuthDI();
di.registerSingleton<DioRemote>(
DioRemote(baseUrl: 'https://everestacademy.ir/'),
instanceName: 'baseRemote',
);
}
Future<void> setupInjection() async{
Future<void> setupInjection() async {
await setupChickenDI();
}

View File

@@ -1,5 +1,7 @@
import 'package:flutter/animation.dart';
import 'package:rasadyar_app/presentation/routes/app_pages.dart';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
import 'package:rasadyar_core/core.dart';
@@ -8,6 +10,11 @@ class SplashLogic extends GetxController with GetTickerProviderStateMixin {
late final AnimationController rotateController;
Rxn<Animation<double>> scaleAnimation = Rxn();
Rxn<Animation<double>> rotationAnimation = Rxn();
RxBool hasUpdated = false.obs;
RxBool onUpdateDownload = false.obs;
RxDouble percent = 0.0.obs;
final RxnString _updateFilePath = RxnString();
final platform = MethodChannel('apk_installer');
var tokenService = Get.find<TokenStorageService>();
@@ -24,15 +31,9 @@ class SplashLogic extends GetxController with GetTickerProviderStateMixin {
duration: const Duration(milliseconds: 8000),
);
scaleAnimation.value = Tween<double>(
begin: 0.8,
end: 1.2,
).animate(scaleController);
scaleAnimation.value = Tween<double>(begin: 0.8, end: 1.2).animate(scaleController);
rotationAnimation.value = Tween<double>(
begin: 0.0,
end: 1,
).animate(rotateController);
rotationAnimation.value = Tween<double>(begin: 0.0, end: 1).animate(rotateController);
rotateController.forward();
rotateController.addStatusListener((status) {
@@ -51,21 +52,171 @@ class SplashLogic extends GetxController with GetTickerProviderStateMixin {
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(seconds: 1), () async {
hasUpdated.value = !hasUpdated.value;
//TODO
/*Future.delayed(const Duration(seconds: 1), () async {
var module = tokenService.appModule.value;
Get.offAndToNamed(getTargetPage(module));
});
});*/
}
@override
void onClose() {
rotateController.dispose();
scaleController.dispose();
super.onClose();
}
Future<void> fileDownload() async {
onUpdateDownload.value = true;
Dio dio = Dio();
final dir = await getApplicationDocumentsDirectory();
final filePath = "${dir.path}/app-release.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(
'https://everestacademy.ir/app/app-release.apk',
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;
tLog(filePath);
fLog(_updateFilePath.value);
}
onUpdateDownload.value = false;
}
Future<void> installApk() async {
try {
eLog(_updateFilePath.value);
final dir = await getApplicationDocumentsDirectory();
await platform.invokeMethod('apk_installer', {'appPath': _updateFilePath.value});
} catch (e) {
print("خطا در نصب: $e");
}
}
}

View File

@@ -15,6 +15,7 @@ class SplashPage extends GetView<SplashLogic> {
child: Stack(
alignment: Alignment.center,
children: [
ObxValue((data) {
return ScaleTransition(
scale: data.value!,