diff --git a/android/app/build.gradle b/android/app/build.gradle index 071c50d..6d239c3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -113,4 +113,5 @@ dependencies { implementation "androidx.sqlite:sqlite-framework:2.1.0" implementation "androidx.sqlite:sqlite:2.1.0" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + implementation 'androidx.appcompat:appcompat:1.6.1' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c841e84..1ba4137 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -74,6 +74,11 @@ + + + () + for (resource in requestedResources) { + when (resource) { + PermissionRequest.RESOURCE_VIDEO_CAPTURE -> { + if (ContextCompat.checkSelfPermission(this@FullscreenWebViewActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + permissionsNeeded.add(Manifest.permission.CAMERA) + } + } + PermissionRequest.RESOURCE_AUDIO_CAPTURE -> { + if (ContextCompat.checkSelfPermission(this@FullscreenWebViewActivity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + permissionsNeeded.add(Manifest.permission.RECORD_AUDIO) + } + } + } + } + + if (permissionsNeeded.isNotEmpty()) { + // درخواست پرمیژن runtime + pendingPermissionRequest = request + ActivityCompat.requestPermissions(this@FullscreenWebViewActivity, permissionsNeeded.toTypedArray(), PERMISSION_REQUEST_CODE) + } else { + // پرمیژن ها داده شده، قبول کن درخواست رو + request.grant(request.resources) + } + } + } + } + + webView.loadUrl(url) + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + if (requestCode == PERMISSION_REQUEST_CODE) { + var allGranted = true + for (result in grantResults) { + if (result != PackageManager.PERMISSION_GRANTED) { + allGranted = false + break + } + } + if (allGranted) { + pendingPermissionRequest?.grant(pendingPermissionRequest?.resources) + } else { + pendingPermissionRequest?.deny() + } + pendingPermissionRequest = null + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } + } + + override fun onDestroy() { + super.onDestroy() + webView.destroy() + } +} diff --git a/android/app/src/main/kotlin/com/example/didvan/MainActivity.kt b/android/app/src/main/kotlin/com/example/didvan/MainActivity.kt index cfe4bf5..0dd0cc0 100644 --- a/android/app/src/main/kotlin/com/example/didvan/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/didvan/MainActivity.kt @@ -1,20 +1,87 @@ package com.didvan.didvanapp -import android.app.NotificationChannel -import android.app.NotificationManager +import android.content.ContentValues +import android.content.Context +import android.content.Intent import android.os.Build import android.os.Bundle -import android.os.PersistableBundle +import android.provider.MediaStore +import android.util.Log import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugins.GeneratedPluginRegistrant +import io.flutter.plugin.common.MethodChannel +import java.io.File +import java.io.FileInputStream class MainActivity: FlutterActivity() { - @Override - override fun configureFlutterEngine(flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine) + private val CHANNEL = "com.didvan.shareFile" + + @Override + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> + when (call.method) { + "copyToDownloads" -> { + val filePath = call.argument("filePath") ?: return@setMethodCallHandler + val fileName = call.argument("fileName") ?: return@setMethodCallHandler + + val success = copyFileToDownloads(filePath, fileName) + if (success) { + result.success("File copied successfully") + } else { + result.error("FILE_COPY_ERROR", "Failed to copy file", null) + } + } + "openWebView" -> { + val url = call.argument("url") ?: return@setMethodCallHandler + openWebView(url) + result.success(null) + } + else -> result.notImplemented() + } + } } + private fun copyFileToDownloads(filePath: String, fileName: String): Boolean { + return try { + val context: Context = applicationContext + val file = File(filePath) + if (!file.exists()) return false + + val resolver = context.contentResolver + val contentValues = ContentValues().apply { + put(MediaStore.Downloads.DISPLAY_NAME, fileName) + put(MediaStore.Downloads.MIME_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + put(MediaStore.Downloads.IS_PENDING, 1) + } + + val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) + ?: return false + + resolver.openOutputStream(uri).use { outputStream -> + FileInputStream(file).use { inputStream -> + inputStream.copyTo(outputStream!!) + } + } + + contentValues.clear() + contentValues.put(MediaStore.Downloads.IS_PENDING, 0) + resolver.update(uri, contentValues, null, null) + + true + } catch (e: Exception) { + Log.e("FileCopy", "Error copying file to downloads", e) + false + } + } + + private fun openWebView(url: String) { + Log.d("MainActivity", "Opening WebView with URL: $url") + val intent = Intent(this, FullscreenWebViewActivity::class.java) + intent.putExtra("url", url) + startActivity(intent) + } } diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 08abd17..2e8bc2c 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,17 +1,9 @@ - - + @@ -25,4 +17,9 @@ ?android:attr/colorBackground ?android:attr/textColorPrimary + + + diff --git a/lib/services/webview.dart b/lib/services/webview.dart new file mode 100644 index 0000000..9a6600b --- /dev/null +++ b/lib/services/webview.dart @@ -0,0 +1,13 @@ +import 'package:flutter/services.dart'; + +class NativeWebViewLauncher { + static const MethodChannel _channel = MethodChannel('com.didvan.shareFile'); + + static Future openWebView(String url) async { + try { + await _channel.invokeMethod('openWebView', {'url': url}); + } on PlatformException catch (e) { + print("Failed to open native webview: '${e.message}'."); + } + } +} diff --git a/lib/views/home/main/widgets/main_content.dart b/lib/views/home/main/widgets/main_content.dart index d0de86a..5fe9349 100644 --- a/lib/views/home/main/widgets/main_content.dart +++ b/lib/views/home/main/widgets/main_content.dart @@ -3,6 +3,7 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/views/home/main/widgets/banner.dart'; import 'package:didvan/views/home/widgets/categories.dart'; +import 'package:didvan/views/widgets/ai_banner.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; diff --git a/lib/views/widgets/ai_banner.dart b/lib/views/widgets/ai_banner.dart new file mode 100644 index 0000000..2efb873 --- /dev/null +++ b/lib/views/widgets/ai_banner.dart @@ -0,0 +1,31 @@ +import 'package:didvan/services/webview.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/svg.dart'; + +class AiBanner extends StatelessWidget { + const AiBanner({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + NativeWebViewLauncher.openWebView( + 'https://www.aisada.ir/app/page1.html'); + }, + child: Padding( + padding: const EdgeInsets.only(top: 20), + child: Stack( + children: [ + Icon(Icons.insert_comment_sharp), + Positioned( + left: 15, + child: Icon(Icons.import_contacts)) + ], + ), + ), + ); + } +}