diff --git a/android/app/src/main/kotlin/es/freetimelab/pluriwave/MainActivity.kt b/android/app/src/main/kotlin/es/freetimelab/pluriwave/MainActivity.kt index 9cbc636..7659f3b 100644 --- a/android/app/src/main/kotlin/es/freetimelab/pluriwave/MainActivity.kt +++ b/android/app/src/main/kotlin/es/freetimelab/pluriwave/MainActivity.kt @@ -1,12 +1,16 @@ package es.freetimelab.pluriwave import android.Manifest +import android.app.ActivityNotFoundException import android.content.Intent import android.content.pm.PackageManager +import android.net.Uri import android.media.audiofx.Visualizer import android.os.Build +import android.os.Environment import android.os.Handler import android.os.Looper +import android.provider.DocumentsContract import androidx.core.app.NotificationManagerCompat import com.ryanheise.audioservice.AudioServiceActivity import io.flutter.embedding.engine.FlutterEngine @@ -16,6 +20,7 @@ import io.flutter.plugin.common.MethodChannel class MainActivity : AudioServiceActivity() { private val visualizerChannel = "pluriwave/audio_visualizer" private val alarmChannel = "pluriwave/alarm_scheduler" + private val fileActionsChannel = "pluriwave/file_actions" private val permissionRequestCode = 4821 private var visualizer: Visualizer? = null private var pendingSink: EventChannel.EventSink? = null @@ -96,6 +101,23 @@ class MainActivity : AudioServiceActivity() { else -> result.notImplemented() } } + + MethodChannel( + flutterEngine.dartExecutor.binaryMessenger, + fileActionsChannel + ).setMethodCallHandler { call, result -> + when (call.method) { + "openDirectory" -> { + val path = call.argument("path") + if (path.isNullOrBlank()) { + result.success(false) + } else { + result.success(openDirectory(path)) + } + } + else -> result.notImplemented() + } + } } override fun onNewIntent(intent: Intent) { @@ -122,6 +144,38 @@ class MainActivity : AudioServiceActivity() { ) } + private fun openDirectory(path: String): Boolean { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { + addFlags( + Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION or + Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or + Intent.FLAG_GRANT_PREFIX_URI_PERMISSION + ) + directoryTreeUri(path)?.let { putExtra(DocumentsContract.EXTRA_INITIAL_URI, it) } + } + return try { + startActivity(intent) + true + } catch (_: ActivityNotFoundException) { + false + } catch (_: Throwable) { + false + } + } + + private fun directoryTreeUri(path: String): Uri? { + val external = Environment.getExternalStorageDirectory()?.absolutePath ?: return null + if (!path.startsWith(external)) return null + + val relative = path.removePrefix(external).trimStart('/') + val documentId = if (relative.isBlank()) "primary:" else "primary:$relative" + return DocumentsContract.buildTreeDocumentUri( + "com.android.externalstorage.documents", + documentId + ) + } + private fun startVisualizerWhenAllowed() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.RECORD_AUDIO) diff --git a/lib/estado/estado_radio.dart b/lib/estado/estado_radio.dart index b2a6fbf..ef222c4 100644 --- a/lib/estado/estado_radio.dart +++ b/lib/estado/estado_radio.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import 'package:path_provider/path_provider.dart'; @@ -23,6 +24,10 @@ enum OrdenEmisoras { nombre, calidad } /// Estado global de la app con ChangeNotifier (Provider). class EstadoRadio extends ChangeNotifier { + static const MethodChannel _fileActionsChannel = MethodChannel( + 'pluriwave/file_actions', + ); + EstadoRadio({ ServicioAudio? audio, ServicioFavoritos? favoritos, @@ -626,6 +631,13 @@ class EstadoRadio extends ChangeNotifier { Future abrirDirectorioGrabacion() async { final ruta = await directorioGrabacionEfectivo(); await Directory(ruta).create(recursive: true); + if (!kIsWeb && Platform.isAndroid) { + final abierto = await _fileActionsChannel.invokeMethod( + 'openDirectory', + {'path': ruta}, + ); + return abierto ?? false; + } final uri = Uri.directory(ruta); return launchUrl(uri, mode: LaunchMode.externalApplication); }