fix(recordings): open last file on android
This commit is contained in:
@@ -68,6 +68,16 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/pluriwave_file_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
|
|||||||
@@ -5,13 +5,19 @@ import android.app.PendingIntent
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
|
||||||
class AlarmScheduler(private val context: Context) {
|
class AlarmScheduler(private val context: Context) {
|
||||||
|
private val tag = "PluriWave"
|
||||||
private val alarmManager =
|
private val alarmManager =
|
||||||
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
|
|
||||||
fun scheduleAlarm(id: String, title: String, triggerAtMillis: Long, preNoticeAtMillis: Long) {
|
fun scheduleAlarm(id: String, title: String, triggerAtMillis: Long, preNoticeAtMillis: Long) {
|
||||||
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"alarm.schedule id=$id title=$title triggerAtMillis=$triggerAtMillis preNoticeAtMillis=$preNoticeAtMillis canExact=${canScheduleExactAlarms()}"
|
||||||
|
)
|
||||||
val alarmIntent = PendingIntent.getBroadcast(
|
val alarmIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
requestCode(id, 1),
|
requestCode(id, 1),
|
||||||
@@ -39,6 +45,7 @@ class AlarmScheduler(private val context: Context) {
|
|||||||
AlarmManager.AlarmClockInfo(triggerAtMillis, showIntent),
|
AlarmManager.AlarmClockInfo(triggerAtMillis, showIntent),
|
||||||
alarmIntent
|
alarmIntent
|
||||||
)
|
)
|
||||||
|
Log.d(tag, "alarm.schedule setAlarmClock OK id=$id")
|
||||||
|
|
||||||
if (preNoticeAtMillis > System.currentTimeMillis()) {
|
if (preNoticeAtMillis > System.currentTimeMillis()) {
|
||||||
try {
|
try {
|
||||||
@@ -56,13 +63,18 @@ class AlarmScheduler(private val context: Context) {
|
|||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
Log.d(tag, "alarm.schedule preNotice OK id=$id")
|
||||||
} catch (_: SecurityException) {
|
} catch (_: SecurityException) {
|
||||||
// The main alarm is already scheduled with setAlarmClock.
|
// The main alarm is already scheduled with setAlarmClock.
|
||||||
|
Log.w(tag, "alarm.schedule preNotice SecurityException id=$id")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(tag, "alarm.schedule preNotice skipped id=$id")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelAlarm(id: String) {
|
fun cancelAlarm(id: String) {
|
||||||
|
Log.d(tag, "alarm.cancel id=$id")
|
||||||
for (slot in 1..3) {
|
for (slot in 1..3) {
|
||||||
alarmManager.cancel(
|
alarmManager.cancel(
|
||||||
PendingIntent.getBroadcast(
|
PendingIntent.getBroadcast(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package es.freetimelab.pluriwave
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.ActivityNotFoundException
|
import android.app.ActivityNotFoundException
|
||||||
|
import android.content.ClipData
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -11,13 +12,17 @@ import android.os.Environment
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
import com.ryanheise.audioservice.AudioServiceActivity
|
import com.ryanheise.audioservice.AudioServiceActivity
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
import io.flutter.plugin.common.EventChannel
|
import io.flutter.plugin.common.EventChannel
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class MainActivity : AudioServiceActivity() {
|
class MainActivity : AudioServiceActivity() {
|
||||||
|
private val tag = "PluriWave"
|
||||||
private val visualizerChannel = "pluriwave/audio_visualizer"
|
private val visualizerChannel = "pluriwave/audio_visualizer"
|
||||||
private val alarmChannel = "pluriwave/alarm_scheduler"
|
private val alarmChannel = "pluriwave/alarm_scheduler"
|
||||||
private val fileActionsChannel = "pluriwave/file_actions"
|
private val fileActionsChannel = "pluriwave/file_actions"
|
||||||
@@ -59,7 +64,9 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
val title = call.argument<String>("title") ?: "PluriWave"
|
val title = call.argument<String>("title") ?: "PluriWave"
|
||||||
val triggerAtMillis = call.argument<Long>("triggerAtMillis")
|
val triggerAtMillis = call.argument<Long>("triggerAtMillis")
|
||||||
val preNoticeAtMillis = call.argument<Long>("preNoticeAtMillis") ?: 0L
|
val preNoticeAtMillis = call.argument<Long>("preNoticeAtMillis") ?: 0L
|
||||||
|
Log.d(tag, "alarm.channel scheduleAlarm id=$id triggerAtMillis=$triggerAtMillis preNoticeAtMillis=$preNoticeAtMillis")
|
||||||
if (id == null || triggerAtMillis == null) {
|
if (id == null || triggerAtMillis == null) {
|
||||||
|
Log.w(tag, "alarm.channel scheduleAlarm invalid id=$id triggerAtMillis=$triggerAtMillis")
|
||||||
result.error("INVALID_ALARM", "Missing alarm id or trigger time", null)
|
result.error("INVALID_ALARM", "Missing alarm id or trigger time", null)
|
||||||
} else {
|
} else {
|
||||||
alarmScheduler.scheduleAlarm(id, title, triggerAtMillis, preNoticeAtMillis)
|
alarmScheduler.scheduleAlarm(id, title, triggerAtMillis, preNoticeAtMillis)
|
||||||
@@ -68,6 +75,7 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
}
|
}
|
||||||
"cancelAlarm" -> {
|
"cancelAlarm" -> {
|
||||||
val id = call.argument<String>("id")
|
val id = call.argument<String>("id")
|
||||||
|
Log.d(tag, "alarm.channel cancelAlarm id=$id")
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
result.error("INVALID_ALARM", "Missing alarm id", null)
|
result.error("INVALID_ALARM", "Missing alarm id", null)
|
||||||
} else {
|
} else {
|
||||||
@@ -77,6 +85,7 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
}
|
}
|
||||||
"dismissAlarmNotification" -> {
|
"dismissAlarmNotification" -> {
|
||||||
val id = call.argument<String>("id")
|
val id = call.argument<String>("id")
|
||||||
|
Log.d(tag, "alarm.channel dismissAlarmNotification id=$id")
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
result.error("INVALID_ALARM", "Missing alarm id", null)
|
result.error("INVALID_ALARM", "Missing alarm id", null)
|
||||||
} else {
|
} else {
|
||||||
@@ -85,6 +94,7 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"diagnostics" -> {
|
"diagnostics" -> {
|
||||||
|
Log.d(tag, "alarm.channel diagnostics")
|
||||||
result.success(
|
result.success(
|
||||||
mapOf(
|
mapOf(
|
||||||
"canScheduleExactAlarms" to alarmScheduler.canScheduleExactAlarms(),
|
"canScheduleExactAlarms" to alarmScheduler.canScheduleExactAlarms(),
|
||||||
@@ -95,7 +105,9 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
"getInitialAlarmIntent" -> {
|
"getInitialAlarmIntent" -> {
|
||||||
result.success(alarmPayload(intent))
|
val payload = alarmPayload(intent)
|
||||||
|
Log.d(tag, "alarm.channel getInitialAlarmIntent payload=$payload")
|
||||||
|
result.success(payload)
|
||||||
intent?.removeExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_ACTION)
|
intent?.removeExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_ACTION)
|
||||||
}
|
}
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
@@ -109,12 +121,23 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
when (call.method) {
|
when (call.method) {
|
||||||
"openDirectory" -> {
|
"openDirectory" -> {
|
||||||
val path = call.argument<String>("path")
|
val path = call.argument<String>("path")
|
||||||
|
Log.d(tag, "file_actions.openDirectory path=$path")
|
||||||
if (path.isNullOrBlank()) {
|
if (path.isNullOrBlank()) {
|
||||||
result.success(false)
|
result.success(false)
|
||||||
} else {
|
} else {
|
||||||
result.success(openDirectory(path))
|
result.success(openDirectory(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"openFile" -> {
|
||||||
|
val path = call.argument<String>("path")
|
||||||
|
val mimeType = call.argument<String>("mimeType") ?: "audio/*"
|
||||||
|
Log.d(tag, "file_actions.openFile path=$path mimeType=$mimeType")
|
||||||
|
if (path.isNullOrBlank()) {
|
||||||
|
result.success(false)
|
||||||
|
} else {
|
||||||
|
result.success(openFile(path, mimeType))
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,6 +148,7 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
setIntent(intent)
|
setIntent(intent)
|
||||||
val payload = alarmPayload(intent)
|
val payload = alarmPayload(intent)
|
||||||
if (payload.isNotEmpty()) {
|
if (payload.isNotEmpty()) {
|
||||||
|
Log.d(tag, "alarm.channel onNewIntent payload=$payload")
|
||||||
alarmMethodChannel?.invokeMethod("alarmFired", payload)
|
alarmMethodChannel?.invokeMethod("alarmFired", payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,14 +180,46 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
}
|
}
|
||||||
return try {
|
return try {
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
Log.d(tag, "file_actions.openDirectory launched path=$path")
|
||||||
true
|
true
|
||||||
} catch (_: ActivityNotFoundException) {
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
Log.w(tag, "file_actions.openDirectory no activity for path=$path")
|
||||||
false
|
false
|
||||||
} catch (_: Throwable) {
|
} catch (error: Throwable) {
|
||||||
|
Log.e(tag, "file_actions.openDirectory failed path=$path", error)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openFile(path: String, mimeType: String): Boolean {
|
||||||
|
val file = File(path)
|
||||||
|
if (!file.exists()) {
|
||||||
|
Log.w(tag, "file_actions.openFile missing path=$path")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
val uri = FileProvider.getUriForFile(
|
||||||
|
this,
|
||||||
|
"$packageName.fileprovider",
|
||||||
|
file
|
||||||
|
)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
setDataAndType(uri, mimeType)
|
||||||
|
clipData = ClipData.newUri(contentResolver, "recording", uri)
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
startActivity(Intent.createChooser(intent, "Abrir grabación"))
|
||||||
|
Log.d(tag, "file_actions.openFile launched path=$path")
|
||||||
|
true
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
Log.w(tag, "file_actions.openFile no viewer path=$path; opening parent")
|
||||||
|
openDirectory(file.parentFile?.absolutePath ?: path)
|
||||||
|
} catch (error: Throwable) {
|
||||||
|
Log.e(tag, "file_actions.openFile failed path=$path; opening parent", error)
|
||||||
|
openDirectory(file.parentFile?.absolutePath ?: path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun directoryTreeUri(path: String): Uri? {
|
private fun directoryTreeUri(path: String): Uri? {
|
||||||
val external = Environment.getExternalStorageDirectory()?.absolutePath ?: return null
|
val external = Environment.getExternalStorageDirectory()?.absolutePath ?: return null
|
||||||
if (!path.startsWith(external)) return null
|
if (!path.startsWith(external)) return null
|
||||||
|
|||||||
@@ -7,13 +7,18 @@ import android.content.BroadcastReceiver
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
|
||||||
class PluriWaveAlarmReceiver : BroadcastReceiver() {
|
class PluriWaveAlarmReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
val alarmId = intent.getStringExtra(EXTRA_ALARM_ID) ?: return
|
val alarmId = intent.getStringExtra(EXTRA_ALARM_ID) ?: run {
|
||||||
|
Log.w(TAG, "alarm.receiver missing alarmId action=${intent.action}")
|
||||||
|
return
|
||||||
|
}
|
||||||
val title = intent.getStringExtra(EXTRA_ALARM_TITLE) ?: "PluriWave"
|
val title = intent.getStringExtra(EXTRA_ALARM_TITLE) ?: "PluriWave"
|
||||||
|
Log.d(TAG, "alarm.receiver action=${intent.action} id=$alarmId title=$title")
|
||||||
|
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
ACTION_FIRE -> {
|
ACTION_FIRE -> {
|
||||||
@@ -24,7 +29,12 @@ class PluriWaveAlarmReceiver : BroadcastReceiver() {
|
|||||||
putExtra(EXTRA_ALARM_ACTION, ACTION_FIRE)
|
putExtra(EXTRA_ALARM_ACTION, ACTION_FIRE)
|
||||||
}
|
}
|
||||||
showFireNotification(context, alarmId, title, launch)
|
showFireNotification(context, alarmId, title, launch)
|
||||||
context.startActivity(launch)
|
try {
|
||||||
|
context.startActivity(launch)
|
||||||
|
Log.d(TAG, "alarm.receiver fire startActivity OK id=$alarmId")
|
||||||
|
} catch (error: Throwable) {
|
||||||
|
Log.e(TAG, "alarm.receiver fire startActivity ERROR id=$alarmId", error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ACTION_PRE_NOTICE -> {
|
ACTION_PRE_NOTICE -> {
|
||||||
showPreNoticeNotification(context, alarmId, title)
|
showPreNoticeNotification(context, alarmId, title)
|
||||||
@@ -37,8 +47,14 @@ class PluriWaveAlarmReceiver : BroadcastReceiver() {
|
|||||||
putExtra(EXTRA_ALARM_TITLE, title)
|
putExtra(EXTRA_ALARM_TITLE, title)
|
||||||
putExtra(EXTRA_ALARM_ACTION, ACTION_SKIP_NEXT)
|
putExtra(EXTRA_ALARM_ACTION, ACTION_SKIP_NEXT)
|
||||||
}
|
}
|
||||||
context.startActivity(launch)
|
try {
|
||||||
|
context.startActivity(launch)
|
||||||
|
Log.d(TAG, "alarm.receiver skipNext startActivity OK id=$alarmId")
|
||||||
|
} catch (error: Throwable) {
|
||||||
|
Log.e(TAG, "alarm.receiver skipNext startActivity ERROR id=$alarmId", error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else -> Log.w(TAG, "alarm.receiver unknown action=${intent.action} id=$alarmId")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +88,9 @@ class PluriWaveAlarmReceiver : BroadcastReceiver() {
|
|||||||
fireNotificationIdForAlarm(alarmId),
|
fireNotificationIdForAlarm(alarmId),
|
||||||
notification,
|
notification,
|
||||||
)
|
)
|
||||||
} catch (_: SecurityException) {
|
Log.d(TAG, "alarm.notification fire shown id=$alarmId")
|
||||||
|
} catch (error: SecurityException) {
|
||||||
|
Log.e(TAG, "alarm.notification fire SecurityException id=$alarmId", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +132,12 @@ class PluriWaveAlarmReceiver : BroadcastReceiver() {
|
|||||||
.addAction(0, "Omitir siguiente", skipNextIntent)
|
.addAction(0, "Omitir siguiente", skipNextIntent)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
NotificationManagerCompat.from(context).notify(notificationIdForAlarm(alarmId), notification)
|
try {
|
||||||
|
NotificationManagerCompat.from(context).notify(notificationIdForAlarm(alarmId), notification)
|
||||||
|
Log.d(TAG, "alarm.notification preNotice shown id=$alarmId")
|
||||||
|
} catch (error: SecurityException) {
|
||||||
|
Log.e(TAG, "alarm.notification preNotice SecurityException id=$alarmId", error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ensureFireChannel(context: Context) {
|
private fun ensureFireChannel(context: Context) {
|
||||||
@@ -155,6 +178,7 @@ class PluriWaveAlarmReceiver : BroadcastReceiver() {
|
|||||||
private fun requestCode(id: String, slot: Int): Int = 47 * id.hashCode() + slot
|
private fun requestCode(id: String, slot: Int): Int = 47 * id.hashCode() + slot
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val TAG = "PluriWave"
|
||||||
const val CHANNEL_ID = "pluriwave_alarm_pre_notice"
|
const val CHANNEL_ID = "pluriwave_alarm_pre_notice"
|
||||||
const val FIRE_CHANNEL_ID = "pluriwave_alarm_fire"
|
const val FIRE_CHANNEL_ID = "pluriwave_alarm_fire"
|
||||||
const val ACTION_FIRE = "es.freetimelab.pluriwave.alarm.FIRE"
|
const val ACTION_FIRE = "es.freetimelab.pluriwave.alarm.FIRE"
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<files-path
|
||||||
|
name="files"
|
||||||
|
path="." />
|
||||||
|
<cache-path
|
||||||
|
name="cache"
|
||||||
|
path="." />
|
||||||
|
<external-files-path
|
||||||
|
name="external_files"
|
||||||
|
path="." />
|
||||||
|
<external-cache-path
|
||||||
|
name="external_cache"
|
||||||
|
path="." />
|
||||||
|
</paths>
|
||||||
@@ -49,17 +49,20 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> inicializar() async {
|
Future<void> inicializar() async {
|
||||||
|
debugPrint('[PluriWave][alarmas] inicializar');
|
||||||
_cargando = true;
|
_cargando = true;
|
||||||
_error = null;
|
_error = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
try {
|
try {
|
||||||
final config = await servicio.cargar();
|
final config = await servicio.cargar();
|
||||||
_aplicar(config);
|
_aplicar(config);
|
||||||
|
debugPrint('[PluriWave][alarmas] cargadas=${_alarmas.length} vacaciones=${_vacaciones.length} excepciones=${_excepciones.length}');
|
||||||
await _sincronizarTodas();
|
await _sincronizarTodas();
|
||||||
await cargarDiagnostico();
|
await cargarDiagnostico();
|
||||||
_activarRefresco();
|
_activarRefresco();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_error = 'No se pudieron cargar las alarmas: $e';
|
_error = 'No se pudieron cargar las alarmas: $e';
|
||||||
|
debugPrint('[PluriWave][alarmas] inicializar ERROR $e');
|
||||||
} finally {
|
} finally {
|
||||||
_cargando = false;
|
_cargando = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@@ -67,10 +70,13 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> guardarAlarma(AlarmaMusical alarma) async {
|
Future<void> guardarAlarma(AlarmaMusical alarma) async {
|
||||||
|
debugPrint('[PluriWave][alarmas] guardar id=${alarma.id} activa=${alarma.activa} hora=${alarma.hora}:${alarma.minuto} tipo=${alarma.tipoProgramacion.name}');
|
||||||
final config = await servicio.guardarAlarma(alarma);
|
final config = await servicio.guardarAlarma(alarma);
|
||||||
_aplicar(config);
|
_aplicar(config);
|
||||||
try {
|
try {
|
||||||
await android.programar(_alarmas.firstWhere((a) => a.id == alarma.id));
|
final guardada = _alarmas.firstWhere((a) => a.id == alarma.id);
|
||||||
|
debugPrint('[PluriWave][alarmas] guardada id=${guardada.id} proxima=${guardada.proximaEjecucion?.toIso8601String()}');
|
||||||
|
await android.programar(guardada);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_error =
|
_error =
|
||||||
'Alarma guardada, pero Android no pudo programarla todavía: $e';
|
'Alarma guardada, pero Android no pudo programarla todavía: $e';
|
||||||
@@ -79,6 +85,7 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> refrescarProgramacion() async {
|
Future<void> refrescarProgramacion() async {
|
||||||
|
debugPrint('[PluriWave][alarmas] refrescar programacion');
|
||||||
final config = await servicio.recalcularTodas();
|
final config = await servicio.recalcularTodas();
|
||||||
_aplicar(config);
|
_aplicar(config);
|
||||||
await _sincronizarTodas();
|
await _sincronizarTodas();
|
||||||
@@ -86,6 +93,7 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> eliminarAlarma(String id) async {
|
Future<void> eliminarAlarma(String id) async {
|
||||||
|
debugPrint('[PluriWave][alarmas] eliminar id=$id');
|
||||||
final config = await servicio.eliminarAlarma(id);
|
final config = await servicio.eliminarAlarma(id);
|
||||||
_aplicar(config);
|
_aplicar(config);
|
||||||
await android.cancelar(id);
|
await android.cancelar(id);
|
||||||
@@ -97,6 +105,7 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saltarProxima(String alarmaId) async {
|
Future<void> saltarProxima(String alarmaId) async {
|
||||||
|
debugPrint('[PluriWave][alarmas] saltar proxima id=$alarmaId');
|
||||||
final config = await servicio.saltarProxima(alarmaId);
|
final config = await servicio.saltarProxima(alarmaId);
|
||||||
_aplicar(config);
|
_aplicar(config);
|
||||||
AlarmaMusical? alarma;
|
AlarmaMusical? alarma;
|
||||||
@@ -113,6 +122,7 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> guardarVacaciones(List<RangoVacaciones> vacaciones) async {
|
Future<void> guardarVacaciones(List<RangoVacaciones> vacaciones) async {
|
||||||
|
debugPrint('[PluriWave][alarmas] guardar vacaciones count=${vacaciones.length}');
|
||||||
final config = await servicio.guardarVacaciones(vacaciones);
|
final config = await servicio.guardarVacaciones(vacaciones);
|
||||||
_aplicar(config);
|
_aplicar(config);
|
||||||
await _sincronizarTodas();
|
await _sincronizarTodas();
|
||||||
@@ -121,11 +131,13 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> posponerAlarma(AlarmaMusical alarma, int minutos) async {
|
Future<void> posponerAlarma(AlarmaMusical alarma, int minutos) async {
|
||||||
final proxima = DateTime.now().add(Duration(minutes: minutos));
|
final proxima = DateTime.now().add(Duration(minutes: minutos));
|
||||||
|
debugPrint('[PluriWave][alarmas] posponer id=${alarma.id} minutos=$minutos proxima=${proxima.toIso8601String()}');
|
||||||
await android.ocultarNotificacionAlarma(alarma.id);
|
await android.ocultarNotificacionAlarma(alarma.id);
|
||||||
await android.programar(alarma.copyWith(proximaEjecucion: proxima));
|
await android.programar(alarma.copyWith(proximaEjecucion: proxima));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> finalizarEjecucion(String alarmaId) async {
|
Future<void> finalizarEjecucion(String alarmaId) async {
|
||||||
|
debugPrint('[PluriWave][alarmas] finalizar ejecucion id=$alarmaId');
|
||||||
await android.ocultarNotificacionAlarma(alarmaId);
|
await android.ocultarNotificacionAlarma(alarmaId);
|
||||||
await refrescarProgramacion();
|
await refrescarProgramacion();
|
||||||
}
|
}
|
||||||
@@ -150,13 +162,15 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
Future<void> cargarDiagnostico() async {
|
Future<void> cargarDiagnostico() async {
|
||||||
try {
|
try {
|
||||||
_diagnostico = await android.diagnostico();
|
_diagnostico = await android.diagnostico();
|
||||||
} catch (_) {
|
} catch (e) {
|
||||||
|
debugPrint('[PluriWave][alarmas] diagnostico ERROR $e');
|
||||||
_diagnostico = null;
|
_diagnostico = null;
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _sincronizarTodas() async {
|
Future<void> _sincronizarTodas() async {
|
||||||
|
debugPrint('[PluriWave][alarmas] sincronizar todas count=${_alarmas.length}');
|
||||||
for (final alarma in _alarmas) {
|
for (final alarma in _alarmas) {
|
||||||
await android.programar(alarma);
|
await android.programar(alarma);
|
||||||
}
|
}
|
||||||
@@ -188,6 +202,7 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
if (proxima.isAfter(ahora)) continue;
|
if (proxima.isAfter(ahora)) continue;
|
||||||
final key = '${alarma.id}:${proxima.millisecondsSinceEpoch}';
|
final key = '${alarma.id}:${proxima.millisecondsSinceEpoch}';
|
||||||
if (_ejecucionesEmitidas.add(key)) {
|
if (_ejecucionesEmitidas.add(key)) {
|
||||||
|
debugPrint('[PluriWave][alarmas] vencida local id=${alarma.id} proxima=${proxima.toIso8601String()}');
|
||||||
_alarmasVencidasController.add(alarma);
|
_alarmasVencidasController.add(alarma);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ class EstadoRadio extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
_resultadosBusqueda = nuevaLista;
|
_resultadosBusqueda = nuevaLista;
|
||||||
// _buscarPaginaFiltrada actualiza offset/hayMas usando p?ginas crudas.
|
// _buscarPaginaFiltrada actualiza offset/hayMas usando páginas crudas.
|
||||||
_hayMasBusqueda = _hayMasBusqueda && pagina.isNotEmpty;
|
_hayMasBusqueda = _hayMasBusqueda && pagina.isNotEmpty;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
_errorController.add('No se pudieron cargar mas emisoras.');
|
_errorController.add('No se pudieron cargar mas emisoras.');
|
||||||
@@ -644,7 +644,21 @@ class EstadoRadio extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<bool> abrirUltimaGrabacion() async {
|
Future<bool> abrirUltimaGrabacion() async {
|
||||||
final archivo = ultimaGrabacion;
|
final archivo = ultimaGrabacion;
|
||||||
if (archivo == null || !await archivo.exists()) return false;
|
if (archivo == null || !await archivo.exists()) {
|
||||||
|
debugPrint('[PluriWave][recordings] last recording missing');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
debugPrint('[PluriWave][recordings] opening last file: ${archivo.path}');
|
||||||
|
if (!kIsWeb && Platform.isAndroid) {
|
||||||
|
final abierto = await _fileActionsChannel.invokeMethod<bool>(
|
||||||
|
'openFile',
|
||||||
|
{
|
||||||
|
'path': archivo.path,
|
||||||
|
'mimeType': 'audio/*',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return abierto ?? false;
|
||||||
|
}
|
||||||
return launchUrl(
|
return launchUrl(
|
||||||
Uri.file(archivo.path),
|
Uri.file(archivo.path),
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import '../modelos/alarma_musical.dart';
|
import '../modelos/alarma_musical.dart';
|
||||||
@@ -64,9 +65,15 @@ class ServicioAlarmasAndroid {
|
|||||||
Future<void> programar(AlarmaMusical alarma) async {
|
Future<void> programar(AlarmaMusical alarma) async {
|
||||||
final proxima = alarma.proximaEjecucion;
|
final proxima = alarma.proximaEjecucion;
|
||||||
if (proxima == null || !alarma.activa) {
|
if (proxima == null || !alarma.activa) {
|
||||||
|
debugPrint(
|
||||||
|
'[PluriWave][alarmas] cancelar por inactiva/sin proxima id=${alarma.id} activa=${alarma.activa} proxima=$proxima',
|
||||||
|
);
|
||||||
await cancelar(alarma.id);
|
await cancelar(alarma.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
debugPrint(
|
||||||
|
'[PluriWave][alarmas] programar id=${alarma.id} nombre=${alarma.nombre} proxima=${proxima.toIso8601String()} preaviso=${proxima.subtract(const Duration(minutes: 30)).toIso8601String()}',
|
||||||
|
);
|
||||||
await _channel.invokeMethod<void>('scheduleAlarm', {
|
await _channel.invokeMethod<void>('scheduleAlarm', {
|
||||||
'id': alarma.id,
|
'id': alarma.id,
|
||||||
'title': alarma.nombre,
|
'title': alarma.nombre,
|
||||||
@@ -77,16 +84,21 @@ class ServicioAlarmasAndroid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> cancelar(String alarmaId) =>
|
Future<void> cancelar(String alarmaId) =>
|
||||||
_channel.invokeMethod<void>('cancelAlarm', {'id': alarmaId});
|
_logAndInvokeVoid('cancelAlarm', {'id': alarmaId});
|
||||||
|
|
||||||
Future<void> ocultarNotificacionAlarma(String alarmaId) => _channel
|
Future<void> ocultarNotificacionAlarma(String alarmaId) =>
|
||||||
.invokeMethod<void>('dismissAlarmNotification', {'id': alarmaId});
|
_logAndInvokeVoid('dismissAlarmNotification', {'id': alarmaId});
|
||||||
|
|
||||||
Future<DiagnosticoAlarmasAndroid> diagnostico() async {
|
Future<DiagnosticoAlarmasAndroid> diagnostico() async {
|
||||||
|
debugPrint('[PluriWave][alarmas] diagnostico android');
|
||||||
final raw = await _channel.invokeMethod<Map<Object?, Object?>>(
|
final raw = await _channel.invokeMethod<Map<Object?, Object?>>(
|
||||||
'diagnostics',
|
'diagnostics',
|
||||||
);
|
);
|
||||||
return DiagnosticoAlarmasAndroid.fromMap(raw ?? const {});
|
final diag = DiagnosticoAlarmasAndroid.fromMap(raw ?? const {});
|
||||||
|
debugPrint(
|
||||||
|
'[PluriWave][alarmas] diagnostico exactas=${diag.puedeProgramarExactas} notificaciones=${diag.notificacionesPermitidas} sdk=${diag.versionSdk} fabricante=${diag.fabricante}',
|
||||||
|
);
|
||||||
|
return diag;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<EventoAlarmaAndroid?> obtenerEventoInicial() async {
|
Future<EventoAlarmaAndroid?> obtenerEventoInicial() async {
|
||||||
@@ -95,9 +107,17 @@ class ServicioAlarmasAndroid {
|
|||||||
);
|
);
|
||||||
if (raw == null || raw.isEmpty) return null;
|
if (raw == null || raw.isEmpty) return null;
|
||||||
final evento = EventoAlarmaAndroid.fromMap(raw);
|
final evento = EventoAlarmaAndroid.fromMap(raw);
|
||||||
|
debugPrint(
|
||||||
|
'[PluriWave][alarmas] evento inicial id=${evento.alarmaId} accion=${evento.accion}',
|
||||||
|
);
|
||||||
return evento.alarmaId.isEmpty ? null : evento;
|
return evento.alarmaId.isEmpty ? null : evento;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _logAndInvokeVoid(String method, Map<String, Object?> args) {
|
||||||
|
debugPrint('[PluriWave][alarmas] $method $args');
|
||||||
|
return _channel.invokeMethod<void>(method, args);
|
||||||
|
}
|
||||||
|
|
||||||
static void _instalarHandler(MethodChannel channel) {
|
static void _instalarHandler(MethodChannel channel) {
|
||||||
if (_handlerInstalado) return;
|
if (_handlerInstalado) return;
|
||||||
_handlerInstalado = true;
|
_handlerInstalado = true;
|
||||||
@@ -107,6 +127,9 @@ class ServicioAlarmasAndroid {
|
|||||||
if (args is Map) {
|
if (args is Map) {
|
||||||
final evento = EventoAlarmaAndroid.fromMap(args);
|
final evento = EventoAlarmaAndroid.fromMap(args);
|
||||||
if (evento.alarmaId.isNotEmpty) {
|
if (evento.alarmaId.isNotEmpty) {
|
||||||
|
debugPrint(
|
||||||
|
'[PluriWave][alarmas] evento nativo id=${evento.alarmaId} accion=${evento.accion}',
|
||||||
|
);
|
||||||
_eventosController.add(evento);
|
_eventosController.add(evento);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user