Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Desarrollo en Delphi para Android (https://www.clubdelphi.com/foros/forumdisplay.php?f=57)
-   -   ScheduleNotification(Notification) no funciona Android 14 (https://www.clubdelphi.com/foros/showthread.php?t=96646)

jhonalone 13-03-2024 18:51:05

ScheduleNotification(Notification) no funciona Android 14
 
Saludos Amigos.
Necesito vuestra ayuda, una vez más.
Estoy desarrollando una agenda de eventos para recordar sucesos futuros, tales como cumpleaños, citas médicas, reuniones, etc.

Me gustaría implementar un sistema local de notificaciones para recordar al usuario con anticipación.

He pensado utilizar el sistema de notificaciones push local del dispositivo para ello.

Antes de incorporarlo a la app de la agenda, estoy probando con el ejemplo de Delphi 11: Object Pascal\Mobile Snippets\Notifications\SendCancelNotification.

He tenido que hablilitar algunos permisos (cono veréis en el codigo más abajo) y alguna bandera en la unidad System.Android.Notification.pas como esta:

TJPendingIntent.JavaClass.getActivity(TAndroidHelper.Context, TGeneratorUniqueID.GenerateID, Intent, TJPendingIntent.JavaClass.FLAG_UPDATE_CURRENT or TJPendingIntent.JavaClass.FLAG_IMMUTABLE);

Lo explico por si alguien quiere que funcionen las notificaciones en versiones Android 13 y superiores.

También he actualizado el SDK a la última versión. (No sé si esto era necesario)

Bien. He conseguido que funcione en las versiones 9 y 10 de Andoid, (que he podido probar, tanto en notificariones inmediatas como diferidas.

El problema está en la versión 14. (Supongo que en la 13 taambién) Estoy probando y las notificaciones inmediatas se emiten correctamente, PERO LAS QUE SON DIFERIDAS EN EL TIEMPO SE PIERDEN Y NO APARECEN.

Y en eso estoy...

Si alguien ha resuelto el problema y tiene a bein comprtirlo se lo agradezco. Quizás ayude también a quienes puedan tener el mismo problema en el futuro.

Gracias anticipadas. Por leerme y ayudarme.
Saludos cordiales a todos.


Esta es la unidad modificada.
Código Delphi [-]
unit uMain;

interface

uses
  System.Actions, System.Classes, System.Notification,
  FMX.ActnList, FMX.Controls, FMX.Controls.Presentation, FMX.Forms, FMX.Memo, 
  FMX.Memo.Types, FMX.ScrollBox, FMX.StdCtrls, FMX.Types, FMX.Dialogs,
  AndroidApi.Helpers, AndroidAPI.JNI.Os, System.Permissions, System.DateUtils;

type
  TNotificationsForm = class(TForm)
    btnSendScheduledNotification: TButton;
    ToolBar1: TToolBar;
    Label1: TLabel;
    btnSendNotificationImmediately: TButton;
    ToolBar2: TToolBar;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    NotificationC: TNotificationCenter;
    Memo1: TMemo;
    ActionList: TActionList;
    ActionSendScheduledNotification: TAction;
    ActionSendNotificationImmediately: TAction;
    ActionCancelScheduled: TAction;
    ActionCancelAllNotifications: TAction;

    procedure NotificationCPermissionRequestResult(Sender: TObject; const AIsGranted: Boolean);
    procedure NotificationCReceiveLocalNotification(Sender: TObject; ANotification: TNotification);
    procedure ActionListExecute(Action: TBasicAction; var Handled: Boolean);
    procedure ActionSendScheduledNotificationExecute(Sender: TObject);
    procedure ActionSendNotificationImmediatelyExecute(Sender: TObject);
    procedure ActionCancelScheduledExecute(Sender: TObject);
    procedure ActionCancelAllNotificationsExecute(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    FPendingAction: TBasicAction;
    procedure rechazado;
  end;

var
  NotificationsForm: TNotificationsForm;
  Read_SD : String;
  Write_SD : String;
  Internet :String;
  Notific: String;
implementation

{$R *.fmx}

uses
  System.SysUtils,
  DW.Consts.Android, DW.Permissions.Helpers;  // Para los nuevos permisos



procedure TNotificationsForm.rechazado;
begin//   Showmessage('Rechazado2'); Sleep(5000);
ShowMessage('Debe conceder todos los permisos antes usar esta aplicación.');
//   Sleep(7000);
  // Halt(1);
end;

procedure TNotificationsForm.NotificationCPermissionRequestResult(Sender: TObject; const AIsGranted: Boolean);
begin
  if AIsGranted and (FPendingAction <> nil) then
  begin
    FPendingAction.Execute;
  end;

  FPendingAction := nil;
end;

procedure TNotificationsForm.NotificationCReceiveLocalNotification(Sender: TObject; ANotification: TNotification);
begin
  Memo1.Lines.Add(ANotification.AlertBody);
end;

procedure TNotificationsForm.ActionListExecute(Action: TBasicAction; var Handled: Boolean);
begin
  if NotificationC.AuthorizationStatus <> TAuthorizationStatus.Authorized then
  begin
    Handled := True;
    FPendingAction := Action;

    NotificationC.RequestPermission;
  end;
end;

procedure TNotificationsForm.ActionSendScheduledNotificationExecute(Sender: TObject);
begin
var vNotifiCenter:= TNotificationCenter.Create(nil);
  try
    if vNotifiCenter.Supported then
    begin
      var LChannel := vNotifiCenter.CreateChannel('MyChannel', 'MyChannel', 'My Channel');
      try
        LChannel.Importance := TImportance.High;
        vNotifiCenter.CreateOrUpdateChannel(LChannel);
      finally
        LChannel.Free;
      end;
      var vNotification := vNotifiCenter.CreateNotification;
      try
        vNotification.AlertBody := 'Este es mi mensaje';
        vNotification.Title := 'Recuerde';
        vNotification.EnableSound := true;
        vNotification.ChannelId := 'MyChannel';
        vNotification.FireDate := EncodeDateTime(2024,3,12,17,10,0,0);
        vNotifiCenter.ScheduleNotification(vNotification);

      finally
        vNotification.Free;
      end;
    end;
  finally
    vNotifiCenter.Free;
  end;
end;

procedure TNotificationsForm.FormCreate(Sender: TObject);
begin
  Read_SD := JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE);
  Write_SD := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);
  Internet := JStringToString(TJManifest_permission.JavaClass.INTERNET);
  Notific := 'android.permission.POST_NOTIFICATIONS';
end;

procedure TNotificationsForm.FormShow(Sender: TObject);
begin
  begin
   PermissionsService.RequestPermissions([Read_SD, Write_SD,Internet,Notific],
        procedure(const APermissions: TPermissionArray; const AGrantResults: TPermissionStatusArray)
        begin
       // ShowMessage( Length(AGrantResults).ToString);
         if (Length(AGrantResults) = 4) then
  begin
   // permitido;
  //  ShowMessage('Se aceptaron todos los permisos.');
  end
  else
  begin
    rechazado;
    exit; /// No permitimos que haga lo que viene MAS ABAJO
   // ShowMessage('NO Se aceptaron todos los permisos.');
  end;
        end
      );
end;
end;

procedure TNotificationsForm.ActionSendNotificationImmediatelyExecute(Sender: TObject);
begin
var vNotifiCenter:= TNotificationCenter.Create(nil);
  try
    if vNotifiCenter.Supported then
    begin
      var LChannel := vNotifiCenter.CreateChannel('MyChannel', 'MyChannel', 'My Channel');
      try
        LChannel.Importance := TImportance.High;
        vNotifiCenter.CreateOrUpdateChannel(LChannel);
      finally
        LChannel.Free;
      end;
      var vNotification := vNotifiCenter.CreateNotification;
      try
        vNotification.AlertBody := 'Este es mi mensaje';
        vNotification.Title := 'Recuerde';
        vNotification.EnableSound := true;
        vNotification.ChannelId := 'MyChannel';
       vNotification.FireDate := Now;
        vNotifiCenter.PresentNotification(vNotification);
      finally
        vNotification.Free;
      end;
    end;
  finally
    vNotifiCenter.Free;
  end;
end;

procedure TNotificationsForm.ActionCancelScheduledExecute(Sender: TObject);
begin
  { Providing the fact that you already have a MyNotification previously issued }
  NotificationC.CancelNotification('MyNotification');
end;

procedure TNotificationsForm.ActionCancelAllNotificationsExecute(Sender: TObject);
begin
  NotificationC.CancelAll;
end;

end.

Y éste el archivo del Manifiesto modificado.
Código:

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="%package%"
    android:versionCode="%versionCode%"
    android:versionName="%versionName%"
    android:installLocation="%installLocation%">
    <uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="34" />
<%uses-permission%>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
    <queries>
<%queries-child-elements%>
    </queries>
    <application
        android:persistent="%persistent%"
        android:restoreAnyVersion="%restoreAnyVersion%"
        android:label="%label%"
        android:debuggable="%debuggable%"
        android:largeHeap="%largeHeap%"
        android:icon="%icon%"
        android:theme="%theme%"
        android:hardwareAccelerated="%hardwareAccelerated%"
        android:resizeableActivity="false"
        android:requestLegacyExternalStorage="true">
<%provider%>
<%application-meta-data%>
<%uses-libraries%>
<%services%>

        <!-- Our activity is a subclass of the built-in NativeActivity framework class.
            This will take care of integrating with our NDK code. -->
        <activity
            android:name="com.embarcadero.firemonkey.FMXNativeActivity"
            android:label="%activityLabel%"
            android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
            android:launchMode="singleTask"
            android:exported="true">
            <!-- Tell NativeActivity the name of our .so -->
            <meta-data android:name="android.app.lib_name" android:value="%libNameValue%" />
            <receiver
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver" android:exported="true"/>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
<%activity%>
<%receivers%>
    </application>
</manifest>
<!-- END_INCLUDE(manifest) -->


jhonalone 18-03-2024 15:35:41

Hola a todos.
No quiero que penséis que estoy ocioso, esperando que alguien me ayude a resolver la cuestión.
Leevo 4 días buscando y buscado en Google, sin resultados positivos y probando y probando... sin ningún resultado.
Si encontrara la solución, no dudéis que la compartiría.
Saludos.

jhonalone 09-04-2024 00:56:15

Hola a todos.
Todavía no me he rendido, pero estoy a punto.

Empiezo a sospechar que el problema es de la versión de Delphi que uso: estoy en la versión Alexandria 11.0.

Se me olvidó comentaros que estoy intentando enviar notificaciones locales y que, curiosamente, cuando se envía una notificación inmediata con:

Código Delphi [-]
        vNotification.FireDate :=  Now ;
        NotificationC.ScheduleNotification(vNotification);

Tampoco se envía si se usa "ScheduleNotification(vNotification)"

Esto contradice lo que Embarcadero publica en sus páginas de ayuda

Copio una parte:

Código:

    By default, FireDate is set to Now, so if you do not change the value of FireDate,
    the notification is fired immediately.

Scheduled Notifications

Notifications can be scheduled to be fired at any time. Set FireDate to the date and time
when you want to fire the notification. 

When scheduling notifications to a particular date and time, you must bear in mind that
 if the FireDate you set has already passed, the notification is fired immediately.

Ésto es lo que me hace sospechar que las versiones de Rad Studio posteriores a la publicación de la versión 13 de Android, es praobable que lo hayan corregido.

Si alguien puede confirmarme este punto, lo agradecería, par no volverme más loco de lo que ya estoy.

Saludos cordiales.

dani36652 19-12-2024 21:44:54

Es un tema de establecer los permisos adecuados... por ahí en otro hilo comparti un ejemplo y justo fue probado en Android 14 con Scheduled notifications. Checa qué permisos están seleccionados en la sección uses permissions de las configuraciones del proyecto.

jhonalone 20-12-2024 17:56:13

Gracias Dani.
Resolví el problema al actualizar a Delphi 12.1
Un saludo muy cordial.

jhonalone 28-06-2025 14:09:37

Hola de nuevo, compañeros sufridores de Google.
¡¡¡LO HA VUELTO A HACER!!!

Revivo este post, porque el problema es el mismo. Perdonar si no hago bien.

El Gran "Metomeentodo" del mundo, fiel a su política de "respeto" con el resto de los mortales y siguiendo con sus "esfuerzos" por mantener siempre la compatibilidad con las versiones anteriores, ha vuelto a cambiar alguna directiva o inventar un nuevo permiso o no sé qué.

No sé si os habéis dado cuenta de que en los mensajes de las notificaciones, en la última actualización de la versión 14, aparece una campanita negra que, al pulsarla, despliega un mensaje con cuatro opciones para repetir la notificación un tiempo después.

Bueno, yo tengo una app que es una especie de agenda que te avisa con notificaciones un tiempo antes del evento que hayas programado y/o te recuerda hasta 3 veces, si lo deseas, el aviso que hayas programado.

Estaba funcionando perfectamente hasta en Android 14, hasta la última actualización del sofware del dispositivo.

Ahora ya no dispara las notificaciones diferidas, ninguna.

Las dispara cuando vuelves a abrir la aplicación, todas juntas.

Sin embargo, si tienes abierta la aplicación sí que las dispara.

Vamos que me ha destrozado todo mi trabajo.

Utilizo la versión 12.1 de Delphi.

Estoy intentando actualizar los SDK pero he probado con los de la versión 12.3 y no se soluciona el problema. He descargado los SDK para el nivel 36 y voy a probar, aunque sabiendo que delphi 12.3 está usando el nivel 35, tengo muy pocas esperanzas.

Por favor, si alguien ha soportado mi pesada introducción y conoce la solución, rogaría que la comparta conmigo.

Un saludo a todos y gracias por leerme.

Neftali [Germán.Estévez] 30-06-2025 08:15:44

No te puedo ayudar en esto, pues hace tiempo que no programo para Android, pero quería agradecerte toda la información y los comentarios publicados en este hilo (y el detalle y la claridad con lo que lo has hecho).
Creo que pueden ser de mucha ayuda a otros compañeros.
^\||/

jhonalone 30-06-2025 13:34:06

Un gran saludo, Neftalí.
Bueno, ya sabes "Cuanto más tíempo dedicado..."
Durante muchos años has sido un GRAN MAESTRO en el desarrollo de aplicaciones con Delphi y tus ayudas han sido MUY GENEROSAS Y MUY ACERTADAS.

¡¡ Muchas gracias pòr todas las vecess que me sacaste de apuros. !!

Lamento perder un colaborador tan VALIOSO.
¡¡ BUENA SUERTE EN TU NUEVA ANDADURA !!
Estoy seguro que serás tan bueno como en Delphi/Android.

¡¡ Hasta siempre, amigo !!

Neftali [Germán.Estévez] 01-07-2025 09:19:11

No te preocupes, no me he ido a ningún sitio...:p
Sigo con Delphi, aunque no programando para Android.

Gracias de todas formas por el comentario.

dani36652 02-07-2025 18:03:34

Cita:

Empezado por jhonalone (Mensaje 565917)
Hola de nuevo, compañeros sufridores de Google.
¡¡¡LO HA VUELTO A HACER!!!

Revivo este post, porque el problema es el mismo. Perdonar si no hago bien.

El Gran "Metomeentodo" del mundo, fiel a su política de "respeto" con el resto de los mortales y siguiendo con sus "esfuerzos" por mantener siempre la compatibilidad con las versiones anteriores, ha vuelto a cambiar alguna directiva o inventar un nuevo permiso o no sé qué.

No sé si os habéis dado cuenta de que en los mensajes de las notificaciones, en la última actualización de la versión 14, aparece una campanita negra que, al pulsarla, despliega un mensaje con cuatro opciones para repetir la notificación un tiempo después.

Bueno, yo tengo una app que es una especie de agenda que te avisa con notificaciones un tiempo antes del evento que hayas programado y/o te recuerda hasta 3 veces, si lo deseas, el aviso que hayas programado.

Estaba funcionando perfectamente hasta en Android 14, hasta la última actualización del sofware del dispositivo.

Ahora ya no dispara las notificaciones diferidas, ninguna.

Las dispara cuando vuelves a abrir la aplicación, todas juntas.

Sin embargo, si tienes abierta la aplicación sí que las dispara.

Vamos que me ha destrozado todo mi trabajo.

Utilizo la versión 12.1 de Delphi.

Estoy intentando actualizar los SDK pero he probado con los de la versión 12.3 y no se soluciona el problema. He descargado los SDK para el nivel 36 y voy a probar, aunque sabiendo que delphi 12.3 está usando el nivel 35, tengo muy pocas esperanzas.

Por favor, si alguien ha soportado mi pesada introducción y conoce la solución, rogaría que la comparta conmigo.

Un saludo a todos y gracias por leerme.

Hola, yo pudiera ayudarte... pero con mucho respeto, no te entendí nada.

¿Puedes detallarme mejor el problema diciéndome qué hace mas bien, qué no hace tu app? Si pudieras añadir un ejemplo de código, mejor.

jhonalone 02-07-2025 19:59:12

2 Archivos Adjunto(s)
Muchas gracias Dani.

Ya sé que eres un experto en el tema de las notificaciones. No hace mucho me sacaste de un GRAN BARRANCO: Notificaciones que no se cancelaban correctamente.

Quizá me extendí demasiado y no haya quedo claro el problema.

La misma agenda de eventos que estaba desarrollando entonces ya estaba operativa.

Funcionaba correctamente en mi Samsung con Android 14 y otras versiones anteriores.

Hace como 15 ó 20 días, siguiendo la recomendación de mi celular descargué e instalé la actualización recomendada.

A partir de entonces, en todas las notificaciones que recibo en el teléfono aparece una campanita negra a la derecha que al pulsarla despliega un ComboBox en el que de da la opción de elegir si quieres que el celular te repita la notificación. (Esta campanita sale en TODAS las notificaciones, tanto las de mi app como cualquiera otra recibida en el celular. (Una de las que adjunto es de Gemail y la otra de la aplicación estándar de mensajería estándar del celular)

Archivo Adjunto 4273

Archivo Adjunto 4274

Tu creas una notificación cualquiera diferida (Scheduled), desde cualquier aplicación generada con Delphi y cierras la app. Pasa el tiempo para el que estaba programada y no se dispara como si no se hubiera programado.

Luego, vuelves a abrir tu app y se dispara al momento de iniciarse la aplicación.

Hacemos otra prueba: creamos otra notificación diferida. Ahora no cerramos nuestra aplicación. Cuando llega su momento (Con la aplicación abierta) SI QUE SE DISPARA A SU HORA PROGRAMADA.

Este es el problema.

Dices que envíe el código. Bueno, he probado con la misma aplicación que subiste a github cuando resolviste el problema de que no se borraban las notificaciones. He probado el código, con tu parche a la unit "System.Android.Notification.pas" y el comportamiento de la app de prueba es el mismo.

Tengo que agradecerte de nuevo la generosidad que demuestras al usar parte de tu tempo para ayudarme.

Si queda alguna duda házmelo saber.

Un afectuoso saludo.

jhonalone 02-07-2025 20:24:40

1 Archivos Adjunto(s)
Hola de nuevo, Dani.
Te mando fotos del Sofware instalado en el teléfono. Por si te sirven de referencia.

Archivo Adjunto 4278

Perdona que no sea más explicito, pero he saturado el espacio que me concede el club y no puedo subir más fotos.

Gracias de nuevo.

Un cordial saludo.

jhonalone 05-07-2025 20:57:47

Hola a todos.

¡¡¡PROBLEMA ENCONTRADO!!!

Después de mucho buscar... probar... estudiar... Vamos t r a b j a r

Por fin he llegado a la conclusión siguiente: EL PROBLEMA ESTÁ EN EL USO DE LA BATERÍA

Dado que Android implementa en los terminales diversos modos de optimizar la batería, a partir de la versión 14, (última versión en mi caso), debe haber incluído (entre otros muchos procesos) las notificaciones de las app's.

Me ha costado mucho llegar a esta conclussión, ya que en mi terminal he estado mirando varias aplicaciones, (todas ellas incluídas de origen). Al pulsar en la información de la app, existe un apartado llamado "Batería" y al entrar en él se despliegan 3 opciones sobre el uso de la bateria: "No restringido", "Optimizado" y "Restringido"

Digo que me ha costado mucho porque las aplicaciones no se ponen de acuerdo y cada una marca una de las tres opciones (no todas la misma...) y (A PESAR DE ELLO) TODAS EMITEN NOTIFICACIONES...

Admito que este hecho me ha despistado bastante.

Bueno, Delphi 12.1 (el que estoy usando) selecciona, por defecto, la segunda opción: "Optimizado", en el momento de la instalación del todas las aplicaciones.

Cosa rara, otras aplicaciones de mensajería, de correo, reloj... etc. con la misma opción de "Optimizado" ESTÁN MANDANDO NOTIFICACIONES.


Esto me ha descolocado muchísimo, hasta que he llegado a la conclusión de que deben tener habilitado el permiso "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"

Perdonad por el rollazo.

Quería comunicaros mis impresiones lo antes posible, por si alguien (especialmente Dani) está buscando la solución.

Voy a ponerme a trabajar en ello y os cuento a ver si hay suerte...

Saludos.

jhonalone 06-07-2025 14:14:39

Hola de nuevo.
Lo siento. "MI GOZO EN UN POZO"

Hbilitando el permiso "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" no es suficiente.

Solo se entragan las notificaciones en el tiempo programado si habilitamos manualmente el modo "No Restringido" de la Batería.

Voy a buscar cómo se puede hacer mediante el software de la app. (Si es posible)

La semana del 7 al 15 de julio me tomo un permiso. Lo digo por si alguien contesta y no le leo.
Saludos a todos.

jhonalone 06-07-2025 20:03:24

Hola a todos.

¡¡¡ PROBLEMA RESUELTO !!!

Por si interesa a alguien, publico la soluciòn, (como es mi costumbre).

Además de solicitar el permiso "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" hay que solicitar el permiso al usuario (al menos una vez), en tiempo de ejecución de esta manera:

Código Delphi [-]
uses
  {$IFDEF ANDROID}
       Androidapi.Jni.GraphicsContentViewText,
       Androidapi.Jni.Net, 
       Androidapi.JNI.Os, 
       Androidapi.Jni.Provider,
       Androidapi.Helpers, 
       Androidapi.Jni.app,
  {$ENDIF}

.....................

var intent:JIntent;
uri:JNet_Uri;

.......................

if TJBuild_VERSION.JavaClass.SDK_INT>=34 //34 ES EL NIVEL DE API DE ANDROID 14+
 then begin
       intent:=TJIntent.Create;
       Intent.setAction(TJSettings.javaClass.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
       Uri:= TJnet_Uri.JavaClass.parse(StringToJString(Concat('package:',  JStringToString(TAndroidHelper.Context.getPackageName))));
       intent.setData(Uri);
       TAndroidHelper.Activity.startActivity(intent);
      end;

¡Feliz veraneo a quien pueda permitírselo!

Neftali [Germán.Estévez] 07-07-2025 08:14:41

Muchas gracias por el feedback una vez resuelto el problema.
^\||/^\||/^\||/^\||/


La franja horaria es GMT +2. Ahora son las 19:06:33.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi