native webview
This commit is contained in:
parent
126f499bd5
commit
898794e1c3
|
|
@ -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'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@
|
|||
</provider>
|
||||
<!-- End FlutterDownloader customization -->
|
||||
|
||||
<activity android:name=".WebActivity" />
|
||||
<activity
|
||||
android:name=".FullscreenWebViewActivity"
|
||||
android:theme="@style/FullscreenWebViewTheme" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
package com.didvan.didvanapp
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.webkit.PermissionRequest
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
class FullscreenWebViewActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var webView: WebView
|
||||
|
||||
private val PERMISSION_REQUEST_CODE = 1234
|
||||
private var pendingPermissionRequest: PermissionRequest? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
webView = WebView(this)
|
||||
setContentView(webView)
|
||||
|
||||
val url = intent.getStringExtra("url") ?: "https://www.google.com"
|
||||
|
||||
val settings = webView.settings
|
||||
settings.javaScriptEnabled = true
|
||||
settings.mediaPlaybackRequiresUserGesture = false
|
||||
settings.allowContentAccess = true
|
||||
settings.allowFileAccess = true
|
||||
settings.domStorageEnabled = true
|
||||
settings.databaseEnabled = true
|
||||
settings.cacheMode = WebSettings.LOAD_DEFAULT
|
||||
|
||||
webView.webViewClient = WebViewClient()
|
||||
|
||||
webView.webChromeClient = object : WebChromeClient() {
|
||||
override fun onPermissionRequest(request: PermissionRequest?) {
|
||||
runOnUiThread {
|
||||
if (request == null) {
|
||||
super.onPermissionRequest(request)
|
||||
return@runOnUiThread
|
||||
}
|
||||
val requestedResources = request.resources
|
||||
val permissionsNeeded = mutableListOf<String>()
|
||||
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<out String>, 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String>("filePath") ?: return@setMethodCallHandler
|
||||
val fileName = call.argument<String>("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<String>("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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
|
|
@ -25,4 +17,9 @@
|
|||
<item name="android:background">?android:attr/colorBackground</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<!-- ✅ نسخه صحیح -->
|
||||
<style name="FullscreenWebViewTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import 'package:flutter/services.dart';
|
||||
|
||||
class NativeWebViewLauncher {
|
||||
static const MethodChannel _channel = MethodChannel('com.didvan.shareFile');
|
||||
|
||||
static Future<void> openWebView(String url) async {
|
||||
try {
|
||||
await _channel.invokeMethod('openWebView', {'url': url});
|
||||
} on PlatformException catch (e) {
|
||||
print("Failed to open native webview: '${e.message}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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';
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue