diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 7c7e1d7..96504e7 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -7,6 +7,14 @@
+
+
+
+
diff --git a/android/app/src/main/kotlin/ir/mnpc/rasadyar/ApkInstaller.kt b/android/app/src/main/kotlin/ir/mnpc/rasadyar/ApkInstaller.kt
new file mode 100644
index 0000000..3fb5dbe
--- /dev/null
+++ b/android/app/src/main/kotlin/ir/mnpc/rasadyar/ApkInstaller.kt
@@ -0,0 +1,294 @@
+package ir.mnpc.rasadyar
+
+import android.app.Activity
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Build
+import android.provider.Settings
+import android.util.Log
+import android.widget.Toast
+import androidx.core.content.FileProvider
+import java.io.File
+import androidx.core.net.toUri
+
+
+class ApkInstaller(private val context: Context) {
+
+
+ companion object {
+ const val INSTALL_REQUEST_CODE = 1001
+ private const val TAG = "ApkInstaller"
+ }
+
+ /**
+ * Install APK with compatibility for Android 5-15
+ */
+ fun installApk(apkFile: File) {
+ when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
+ // Android 8+ (API 26+) - Use PackageInstaller API
+ installWithPackageInstaller(apkFile)
+ }
+
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
+ // Android 7+ (API 24+) - Use FileProvider with Intent
+ installWithFileProvider(apkFile)
+ }
+
+ else -> {
+ // Android 5-6 (API 21-23) - Use file URI with Intent
+ installWithFileUri(apkFile)
+ }
+ }
+ }
+
+ /**
+ * Android 8+ (API 26+) - PackageInstaller API
+ */
+ private fun installWithPackageInstaller(apkFile: File) {
+ try {
+ val packageInstaller = context.packageManager.packageInstaller
+ val params = PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL
+ )
+
+ // Set installer package name for better tracking
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+ params.setInstallReason(PackageManager.INSTALL_REASON_USER)
+ }
+
+ val sessionId = packageInstaller.createSession(params)
+ val session = packageInstaller.openSession(sessionId)
+
+ session.use { activeSession ->
+ apkFile.inputStream().use { inputStream ->
+ activeSession.openWrite("package", 0, apkFile.length()).use { outputStream ->
+ inputStream.copyTo(outputStream)
+ activeSession.fsync(outputStream)
+ }
+ }
+
+ val intent = Intent(context, InstallResultReceiver::class.java).apply {
+ action = "com.yourpackage.INSTALL_RESULT"
+ }
+
+ val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ } else {
+ PendingIntent.FLAG_UPDATE_CURRENT
+ }
+
+ val pendingIntent = PendingIntent.getBroadcast(
+ context, 0, intent, flags
+ )
+
+ activeSession.commit(pendingIntent.intentSender)
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error installing with PackageInstaller", e)
+ // Fallback to intent method
+ installWithFileProvider(apkFile)
+ }
+ }
+
+ /**
+ * Android 7+ (API 24+) - FileProvider with Intent
+ */
+ private fun installWithFileProvider(apkFile: File) {
+ try {
+ val apkUri = FileProvider.getUriForFile(
+ context,
+ "${context.packageName}.fileprovider",
+ apkFile
+ )
+
+ val intent = createInstallIntent(apkUri).apply {
+ flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
+
+ // Additional flags for better compatibility
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
+ putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, context.packageName)
+ }
+ }
+
+ if (context is Activity) {
+ context.startActivityForResult(intent, INSTALL_REQUEST_CODE)
+ } else {
+ context.startActivity(intent)
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error installing with FileProvider", e)
+ // Final fallback for Android 7+
+ installWithFileUri(apkFile)
+ }
+ }
+
+ /**
+ * Android 5-6 (API 21-23) - File URI with Intent
+ */
+ private fun installWithFileUri(apkFile: File) {
+ try {
+ val apkUri = Uri.fromFile(apkFile)
+ val intent = createInstallIntent(apkUri).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+
+ if (context is Activity) {
+ context.startActivityForResult(intent, INSTALL_REQUEST_CODE)
+ } else {
+ context.startActivity(intent)
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error installing with file URI", e)
+ }
+ }
+
+ /**
+ * Create appropriate install intent based on Android version
+ */
+ private fun createInstallIntent(uri: Uri): Intent {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Intent(Intent.ACTION_INSTALL_PACKAGE).apply {
+ data = uri
+ }
+ } else {
+ Intent(Intent.ACTION_VIEW).apply {
+ setDataAndType(uri, "application/vnd.android.package-archive")
+ }
+ }
+ }
+
+ /**
+ * Check if installation from unknown sources is allowed
+ */
+ fun canInstallPackages(): Boolean {
+ return when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
+ context.packageManager.canRequestPackageInstalls()
+ }
+
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> {
+ try {
+ Settings.Secure.getInt(
+ context.contentResolver,
+ Settings.Secure.INSTALL_NON_MARKET_APPS
+ ) == 1
+ } catch (e: Settings.SettingNotFoundException) {
+ false
+ }
+ }
+
+ else -> {
+ // For older versions, assume it's allowed
+ true
+ }
+ }
+ }
+
+ /**
+ * Request permission to install packages
+ */
+ fun requestInstallPermission() {
+ when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
+ if (!context.packageManager.canRequestPackageInstalls()) {
+ val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
+ data = "package:${context.packageName}".toUri()
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ context.startActivity(intent)
+ }
+ }
+
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> {
+ val intent = Intent(Settings.ACTION_SECURITY_SETTINGS).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ context.startActivity(intent)
+ }
+ }
+ }
+
+ /**
+ * Check if APK file is valid
+ */
+ fun isValidApkFile(apkFile: File): Boolean {
+ if (!apkFile.exists() || !apkFile.canRead()) {
+ return false
+ }
+
+ return try {
+ val packageInfo = context.packageManager.getPackageArchiveInfo(
+ apkFile.absolutePath,
+ PackageManager.GET_ACTIVITIES
+ )
+ packageInfo != null
+ } catch (e: Exception) {
+ Log.e(TAG, "Error validating APK file", e)
+ false
+ }
+ }
+
+}
+
+class InstallResultReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)) {
+ PackageInstaller.STATUS_SUCCESS -> {
+ Log.d("InstallResult", "Installation successful")
+ // Handle successful installation
+ Toast.makeText(context, "Installation successful", Toast.LENGTH_SHORT).show()
+ }
+
+ PackageInstaller.STATUS_FAILURE -> {
+ val message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
+ Log.e("InstallResult", "Installation failed: $message")
+ Toast.makeText(context, "Installation failed: $message", Toast.LENGTH_LONG).show()
+ }
+
+ PackageInstaller.STATUS_FAILURE_BLOCKED -> {
+ Log.e("InstallResult", "Installation blocked")
+ Toast.makeText(context, "Installation blocked by system", Toast.LENGTH_LONG).show()
+ }
+
+ PackageInstaller.STATUS_FAILURE_ABORTED -> {
+ Log.e("InstallResult", "Installation aborted")
+ Toast.makeText(context, "Installation was cancelled", Toast.LENGTH_SHORT).show()
+ }
+
+ PackageInstaller.STATUS_FAILURE_INVALID -> {
+ Log.e("InstallResult", "Invalid APK")
+ Toast.makeText(context, "Invalid APK file", Toast.LENGTH_LONG).show()
+ }
+
+ PackageInstaller.STATUS_FAILURE_CONFLICT -> {
+ Log.e("InstallResult", "Installation conflict")
+ Toast.makeText(
+ context,
+ "Installation conflict with existing app",
+ Toast.LENGTH_LONG
+ ).show()
+ }
+
+ PackageInstaller.STATUS_FAILURE_STORAGE -> {
+ Log.e("InstallResult", "Insufficient storage")
+ Toast.makeText(context, "Insufficient storage space", Toast.LENGTH_LONG).show()
+ }
+
+ PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> {
+ Log.e("InstallResult", "Incompatible app")
+ Toast.makeText(context, "App is incompatible with device", Toast.LENGTH_LONG).show()
+ }
+
+ else -> {
+ Log.w("InstallResult", "Unknown status: $status")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/ir/mnpc/rasadyar/MainActivity.kt b/android/app/src/main/kotlin/ir/mnpc/rasadyar/MainActivity.kt
index 5bae765..33565df 100644
--- a/android/app/src/main/kotlin/ir/mnpc/rasadyar/MainActivity.kt
+++ b/android/app/src/main/kotlin/ir/mnpc/rasadyar/MainActivity.kt
@@ -3,16 +3,22 @@ package ir.mnpc.rasadyar
import android.content.Intent
import android.net.Uri
import android.os.Build
-import android.os.Bundle
+import android.provider.Settings
+import android.util.Log
+import androidx.annotation.RequiresApi
import androidx.core.content.FileProvider
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File
+import androidx.core.net.toUri
class MainActivity : FlutterActivity() {
private val CHANNEL = "apk_installer"
+ private val INSTALL_PACKAGES_REQUEST_CODE = 1001
+ val installer = ApkInstaller(this)
+ private val TAG = "cj"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
@@ -21,9 +27,18 @@ class MainActivity : FlutterActivity() {
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL
).setMethodCallHandler { call, result ->
- if (call.method == "installApk") {
+ if (call.method == "apk_installer") {
val apkPath = call.argument("appPath") ?: ""
- installApk(apkPath)
+ Log.i(TAG, "configureFlutterEngine: $apkPath")
+ val apkFile = File(getExternalFilesDir(null), apkPath)
+ Log.i(TAG, "apkFile: $apkFile")
+ Log.i(TAG, "externalStorageDirectory: ${getExternalFilesDir(null)}")
+ /*
+ if (!installer.canInstallPackages()) {
+ installer.requestInstallPermission()
+ } else {
+ installer.installApk(apkFile)
+ }*/
result.success(null)
}
}
@@ -34,24 +49,51 @@ class MainActivity : FlutterActivity() {
private fun installApk(path: String) {
val file = File(path)
- val intent = Intent(Intent.ACTION_VIEW)
- intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
- val apkUri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- val uri = FileProvider.getUriForFile(
- applicationContext,
- "${applicationContext.packageName}.fileprovider",
- file
- )
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- uri
- } else {
- Uri.fromFile(file)
+ if (!file.exists()) {
+ Log.e("jojo", "APK file does not exist: $path")
+ return
}
- intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
- applicationContext.startActivity(intent)
+ // Check if we can install unknown apps (Android 8.0+)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (!packageManager.canRequestPackageInstalls()) {
+ requestInstallPermission()
+ return
+ }
+ }
+ val intent = Intent(Intent.ACTION_VIEW).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ try {
+ val apkUri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ FileProvider.getUriForFile(
+ context,
+ "${context.packageName}.fileprovider",
+ file
+ ).also {
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
+ } else {
+ Uri.fromFile(file)
+ }
+
+ intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
+ context.startActivity(intent)
+
+ } catch (e: Exception) {
+ Log.e("jojo", "installApk error: ${e.message}", e)
+ }
}
+ @RequiresApi(Build.VERSION_CODES.O)
+ private fun requestInstallPermission() {
+ val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
+ data = "package:$packageName".toUri()
+ }
+ startActivityForResult(intent, INSTALL_PACKAGES_REQUEST_CODE)
+ }
+
+
}
diff --git a/android/app/src/main/res/xml/file_paths.xml b/android/app/src/main/res/xml/file_paths.xml
index 03d4219..fa0400e 100644
--- a/android/app/src/main/res/xml/file_paths.xml
+++ b/android/app/src/main/res/xml/file_paths.xml
@@ -1,6 +1,6 @@
-
+
+
+
diff --git a/lib/infrastructure/di/di.dart b/lib/infrastructure/di/di.dart
index 8ef306c..c1aebef 100644
--- a/lib/infrastructure/di/di.dart
+++ b/lib/infrastructure/di/di.dart
@@ -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 setupPreInjection() async{
+Future setupPreInjection() async {
await setupAllCoreProvider();
await setupAuthDI();
-
-
-
+ di.registerSingleton(
+ DioRemote(baseUrl: 'https://everestacademy.ir/'),
+ instanceName: 'baseRemote',
+ );
}
-Future setupInjection() async{
-
+Future setupInjection() async {
await setupChickenDI();
-
-
}
-
-
diff --git a/lib/presentation/pages/splash/logic.dart b/lib/presentation/pages/splash/logic.dart
index 8f26018..305ccac 100644
--- a/lib/presentation/pages/splash/logic.dart
+++ b/lib/presentation/pages/splash/logic.dart
@@ -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> 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');
var tokenService = Get.find();
@@ -24,15 +31,9 @@ class SplashLogic extends GetxController with GetTickerProviderStateMixin {
duration: const Duration(milliseconds: 8000),
);
- scaleAnimation.value = Tween(
- begin: 0.8,
- end: 1.2,
- ).animate(scaleController);
+ scaleAnimation.value = Tween(begin: 0.8, end: 1.2).animate(scaleController);
- rotationAnimation.value = Tween(
- begin: 0.0,
- end: 1,
- ).animate(rotateController);
+ rotationAnimation.value = Tween(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 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 installApk() async {
+ try {
+ eLog(_updateFilePath.value);
+
+ final dir = await getApplicationDocumentsDirectory();
+ await platform.invokeMethod('apk_installer', {'appPath': _updateFilePath.value});
+ } catch (e) {
+ print("خطا در نصب: $e");
+ }
+ }
}
diff --git a/lib/presentation/pages/splash/view.dart b/lib/presentation/pages/splash/view.dart
index 4cd5ce8..140ed32 100644
--- a/lib/presentation/pages/splash/view.dart
+++ b/lib/presentation/pages/splash/view.dart
@@ -15,6 +15,7 @@ class SplashPage extends GetView {
child: Stack(
alignment: Alignment.center,
children: [
+
ObxValue((data) {
return ScaleTransition(
scale: data.value!,
diff --git a/packages/core/lib/core.dart b/packages/core/lib/core.dart
index 6d9a392..eb3c911 100644
--- a/packages/core/lib/core.dart
+++ b/packages/core/lib/core.dart
@@ -1,56 +1,53 @@
library;
-//models
-export 'data/model/pagination_model/pagination_model.dart';
-
+export 'package:android_intent_plus/android_intent.dart';
+export 'package:android_intent_plus/flag.dart';
+export 'package:device_info_plus/device_info_plus.dart';
+export 'package:dio/dio.dart';
//other packages
export 'package:flutter_localizations/flutter_localizations.dart';
export 'package:flutter_map/flutter_map.dart';
export 'package:flutter_map_animations/flutter_map_animations.dart';
export 'package:flutter_rating_bar/flutter_rating_bar.dart';
+export 'package:flutter_screenutil/flutter_screenutil.dart';
export 'package:flutter_secure_storage/flutter_secure_storage.dart';
export 'package:flutter_slidable/flutter_slidable.dart';
export 'package:font_awesome_flutter/font_awesome_flutter.dart';
-export 'package:device_info_plus/device_info_plus.dart';
-export 'package:dio/dio.dart';
-export 'package:pretty_dio_logger/pretty_dio_logger.dart';
-export 'package:flutter_screenutil/flutter_screenutil.dart';
-
//freezed
export 'package:freezed_annotation/freezed_annotation.dart';
export 'package:geolocator/geolocator.dart';
export 'package:get/get.dart' hide FormData, MultipartFile, Response;
-
//di
export 'package:get_it/get_it.dart';
-export 'injection/di.dart';
-
//local storage
export 'package:hive_ce_flutter/hive_flutter.dart';
-export 'infrastructure/local/hive_local_storage.dart';
-
+///image picker
+export 'package:image_picker/image_picker.dart';
//encryption
//export 'package:encrypt/encrypt.dart' show Encrypted;
//Map and location
export 'package:latlong2/latlong.dart';
+export 'package:path_provider/path_provider.dart';
+export 'package:permission_handler/permission_handler.dart' hide ServiceStatus;
export 'package:persian_datetime_picker/persian_datetime_picker.dart';
+export 'package:pretty_dio_logger/pretty_dio_logger.dart';
export 'package:rasadyar_core/presentation/common/common.dart';
export 'package:rasadyar_core/presentation/utils/utils.dart';
export 'package:rasadyar_core/presentation/widget/widget.dart';
+//models
+export 'data/model/pagination_model/pagination_model.dart';
//infrastructure
export 'infrastructure/infrastructure.dart';
-
-///image picker
-export 'package:image_picker/image_picker.dart';
-
-//utils
-export 'utils/logger_utils.dart';
-export 'utils/network/network.dart';
+export 'infrastructure/local/hive_local_storage.dart';
+export 'injection/di.dart';
export 'utils/extension/date_time_utils.dart';
export 'utils/extension/num_utils.dart';
-export 'utils/map_utils.dart';
-export 'utils/route_utils.dart';
export 'utils/extension/string_utils.dart';
+//utils
+export 'utils/logger_utils.dart';
+export 'utils/map_utils.dart';
+export 'utils/network/network.dart';
+export 'utils/route_utils.dart';
export 'utils/separator_input_formatter.dart';
diff --git a/packages/core/lib/infrastructure/remote/dio_remote.dart b/packages/core/lib/infrastructure/remote/dio_remote.dart
index e1d9d73..b5b179e 100644
--- a/packages/core/lib/infrastructure/remote/dio_remote.dart
+++ b/packages/core/lib/infrastructure/remote/dio_remote.dart
@@ -4,14 +4,17 @@ import 'package:rasadyar_core/core.dart';
class DioRemote implements IHttpClient {
String? baseUrl;
late Dio dio;
- final AppInterceptor interceptors;
+ AppInterceptor? interceptors;
- DioRemote({this.baseUrl, required this.interceptors});
+ DioRemote({this.baseUrl, this.interceptors});
@override
Future init() async {
dio = Dio(BaseOptions(baseUrl: baseUrl ?? ''));
- dio.interceptors.add(interceptors);
+ if (interceptors != null) {
+ dio.interceptors.add(interceptors!);
+ }
+
if (kDebugMode) {
dio.interceptors.add(
PrettyDioLogger(
diff --git a/packages/core/lib/presentation/widget/dialog/dialog.dart b/packages/core/lib/presentation/widget/dialog/dialog.dart
index 788ecce..9f489da 100644
--- a/packages/core/lib/presentation/widget/dialog/dialog.dart
+++ b/packages/core/lib/presentation/widget/dialog/dialog.dart
@@ -1,2 +1,3 @@
export 'delete_dialog.dart';
-export 'warning_dialog.dart';
\ No newline at end of file
+export 'warning_dialog.dart';
+export 'update_dialog.dart';
\ No newline at end of file
diff --git a/packages/core/lib/presentation/widget/dialog/update_dialog.dart b/packages/core/lib/presentation/widget/dialog/update_dialog.dart
new file mode 100644
index 0000000..0b4e748
--- /dev/null
+++ b/packages/core/lib/presentation/widget/dialog/update_dialog.dart
@@ -0,0 +1,53 @@
+import 'dart:io';
+
+import 'package:rasadyar_core/core.dart';
+
+Future requiredUpdateDialog({required Future Function() onConfirm}) async {
+ await Get.defaultDialog(
+ barrierDismissible: false,
+ onWillPop: () async => false,
+ title: 'بروزرسانی',
+ middleText: 'برای استفاده از امکانات برنامه لطفا برنامه را بروز رسانی نمایید.',
+ confirm: RElevated(
+ height: 40.h,
+ width: 150.w,
+ text: 'خروج',
+ backgroundColor: AppColor.error,
+ onPressed: () {
+ exit(0);
+ },
+ ),
+
+ cancel: RElevated(
+ height: 40.h,
+ width: 150.w,
+ text: 'بروز رسانی',
+ onPressed: onConfirm,
+ backgroundColor: AppColor.greenNormal,
+ ),
+ );
+}
+
+Future optionalUpdateDialog({required Future Function() onConfirm}) async {
+ await Get.defaultDialog(
+ barrierDismissible: false,
+ onWillPop: () async => false,
+ title: 'بروزرسانی',
+ middleText: 'برای استفاده از امکانات جدید برنامه می توانید آن را بروزرسانی نمایید.',
+ confirm: RElevated(
+ height: 40.h,
+ width: 150.w,
+ text: 'ادامه',
+ backgroundColor: AppColor.error,
+ onPressed: () => Get.back(),
+ ),
+
+ cancel: RElevated(
+ height: 40.h,
+ width: 150.w,
+ text: 'بروز رسانی',
+ onPressed: onConfirm,
+ backgroundColor: AppColor.greenNormal,
+ ),
+ );
+}
diff --git a/packages/core/pubspec.lock b/packages/core/pubspec.lock
index 1965d8f..9cb141c 100644
--- a/packages/core/pubspec.lock
+++ b/packages/core/pubspec.lock
@@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.4.5"
+ android_intent_plus:
+ dependency: "direct main"
+ description:
+ name: android_intent_plus
+ sha256: dfc1fd3a577205ae8f11e990fb4ece8c90cceabbee56fcf48e463ecf0bd6aae3
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.3.0"
archive:
dependency: transitive
description:
@@ -926,7 +934,7 @@ packages:
source: hosted
version: "1.1.0"
path_provider:
- dependency: transitive
+ dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
@@ -977,10 +985,10 @@ packages:
dependency: "direct main"
description:
name: permission_handler
- sha256: "2d070d8684b68efb580a5997eb62f675e8a885ef0be6e754fb9ef489c177470f"
+ sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
url: "https://pub.dev"
source: hosted
- version: "12.0.0+1"
+ version: "12.0.1"
permission_handler_android:
dependency: transitive
description:
diff --git a/packages/core/pubspec.yaml b/packages/core/pubspec.yaml
index 263fceb..852db42 100644
--- a/packages/core/pubspec.yaml
+++ b/packages/core/pubspec.yaml
@@ -37,8 +37,7 @@ dependencies:
hive_ce: ^2.11.1
hive_ce_flutter: ^2.3.0
flutter_secure_storage: ^9.2.4
-
-
+ path_provider: ^2.1.5
#SVG
flutter_svg: ^2.0.17
@@ -57,13 +56,15 @@ dependencies:
get_it: ^8.0.3
#other
- permission_handler: ^12.0.0+1
+ permission_handler: ^12.0.1
persian_datetime_picker: ^3.1.0
encrypt: ^5.0.3
#L10N tools
intl: ^0.20.2
+ #INITENT
+ android_intent_plus: ^5.3.0
#Map
flutter_map: ^8.1.1
diff --git a/packages/inspection/lib/presentation/root/logic.dart b/packages/inspection/lib/presentation/root/logic.dart
index e95a9b3..31bd149 100644
--- a/packages/inspection/lib/presentation/root/logic.dart
+++ b/packages/inspection/lib/presentation/root/logic.dart
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
-import 'package:rasadyar_core/core.dart';
+import 'package:rasadyar_core/core.dart' ;
import 'package:rasadyar_inspection/presentation/action/view.dart';
import 'package:rasadyar_inspection/presentation/filter/view.dart';
import 'package:rasadyar_inspection/presentation/profile/view.dart';
diff --git a/pubspec.lock b/pubspec.lock
index 1bba7de..2e46f88 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.4.5"
+ android_intent_plus:
+ dependency: transitive
+ description:
+ name: android_intent_plus
+ sha256: dfc1fd3a577205ae8f11e990fb4ece8c90cceabbee56fcf48e463ecf0bd6aae3
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.3.0"
archive:
dependency: transitive
description:
@@ -1009,10 +1017,10 @@ packages:
dependency: transitive
description:
name: permission_handler
- sha256: "2d070d8684b68efb580a5997eb62f675e8a885ef0be6e754fb9ef489c177470f"
+ sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
url: "https://pub.dev"
source: hosted
- version: "12.0.0+1"
+ version: "12.0.1"
permission_handler_android:
dependency: transitive
description: