Search code examples
androiddelphinotificationsfiremonkey

Delphi Seattle Android TNotificationCenter CancelAll not working after one notification has fired


I'm using Delphi DX Seattle and testing on an android Device
when a user clicks a button i'm creating multiple scheduled notifications
and when a user clicks another button im clearing the remaining notifications

I cant seem to get the TNotificationCenter.CancelAll to actually cancel notifications after one notification has fired
Similar Question Here

Using the Delphi sample SendCancelNotification as the base code
I'm Changing the "Send Notification Immediately" button to "Send Multiple Notifications" and the code looks like this

procedure TNotificationsForm.btnSendMultipleNotificationsClick(
  Sender: TObject);

  procedure MyNotificationCreate(aNotificationName : String; aSecondDelay : Word);
  var
    Notification: TNotification;
  begin
    if (aSecondDelay < 0) or (aSecondDelay > 60) then
      raise Exception.Create('Seconds must be between 0 and 60');
    { verify if the service is actually supported }
    Notification := NotificationC.CreateNotification;
    try
      Notification.Name := aNotificationName;
      Notification.AlertBody := aNotificationName + ' ran after ' + inttostr(aSecondDelay) + ' seconds';

      { Fired in 10 second }
      Notification.FireDate := Now + EncodeTime(0,0,aSecondDelay,0);

      { Send notification in Notification Center }
      NotificationC.ScheduleNotification(Notification);
    finally
      Notification.DisposeOf;
    end;
  end;
begin
  MyNotificationCreate('First' , 5);
  MyNotificationCreate('Second', 10);
  MyNotificationCreate('Third' , 15);
end;

This sends three notifications after the button has been clicked.
if i click the "Send Multiple Notifications" button and then before 5 seconds click the "cancel all notifications" button the notifications are canceled successfully.

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

But if i click the multi notification button and wait until the first notification has fired then click the "cancel all notifications" button, it doesn't cancel the remaining notifications. e.g. notifications Second and Third still run.

Has anyone come across this? or have any ideas on why the CancelAll event doesn't work after one of the notifications have been sent


Solution

  • I have found the issue and have got it to work
    this solution requires you to not have a numerical number as the first character of your notification name
    if you can think of a better way to resolve this issue please post your answer

    if you are running into the same issue then copy the System.Android.Notification.pas from Source code here file into your project folder and give it a go

    after sending off three or more notifications and the first notification is triggered on android the notifications were loosing the #10 character.

    e.g. one=10#10Two=11#10Three=12 was becoming Two=11Three=12 and then the cancel all was not getting a name match and never canceling two or three

    I have copied the
    Embarcadero\Studio\17.0\source\rtl\common\System.Android.Notification.pas
    file to my project file and modified it to help make more sense of what was happening

    this is the function that i was using to help track down the problem

    function TAndroidPreferenceAdapter.GetAllNotificationsNames: TStringList;
    var
      Notifications: TStringList;
      NotificationsStr: JString;
      I: Integer;
    begin
      Notifications := TStringList.Create;
      NotificationsStr := FPreference.getString(TJNotificationAlarm.JavaClass.SETTINGS_NOTIFICATION_IDS, nil);
      Notifications.Text := JStringToString(NotificationsStr);
      for I := 0 to Notifications.Count - 1 do
        Notifications[I] := ExtractName(Notifications[I]);
    
      Result := Notifications;
    end;
    

    what i ended up doing was to check for more than 1 = character in the string list loop
    if there were more than 1, Delete the original line then loop through the original string from the = character until a non numeric number occurred and re insert that into the string list

    function TAndroidPreferenceAdapter.GetAllNotificationsNamesNonFiltered: TStringList;
    var
      Notifications: TStringList;
      NotificationsStr: JString;
      I: Integer;
      function OccurrencesOfChar(const S: string; const C: char): integer;
      var
        i: Integer;
      begin
        result := 0;
        for i := 1 to Length(S) do
          if S[i] = C then
            inc(result);
      end;
    
      function InsertLineBreaks(const S: string) : String;
      var sNewString, sRemaining : String;
    
          i : integer;
          const validChars = '0123456789';
      begin
        try
          sNewString := '';
          sRemaining := S;
          for I := pos('=',sRemaining,1) to length(sRemaining) -1 do begin
            if pos( sRemaining[i], validChars ) > 0 then begin
              //continue as its still an integer
            end else begin
              sNewString := copy( sRemaining, 0, i);
              sRemaining := copy( sRemaining, i+1, Length(s));
              if OccurrencesOfChar(sRemaining, '=') > 1  then begin
                InsertLineBreaks(sRemaining);
                sRemaining := '';
              end;
              break;
            end;
          end;
          if sNewString <> '' then
            Notifications.Add( sNewString ) ;
          if sRemaining <> '' then
            Notifications.Add( sRemaining ) ;
          Result := sNewString + sRemaining;
        except
          on E:Exception do begin
            e.Message := e.Message + #13#10 + 'fn[TAndroidPreferenceAdapter.GetAllNotificationsNamesNonFiltered.InsertLineBreaks]';
            raise;
          end;
        end;
      end;
    var sTemp : String;
    begin
      Notifications := TStringList.Create;
      NotificationsStr := FPreference.getString(TJNotificationAlarm.JavaClass.SETTINGS_NOTIFICATION_IDS, nil);
      Notifications.Text := JStringToString(NotificationsStr);
    
      for I := 0 to Notifications.Count - 1 do begin
        if OccurrencesOfChar(Notifications[I], '=') > 1 then begin
          sTemp := Notifications[I];
          Notifications.Delete( i );
          InsertLineBreaks( sTemp );
          break;
        end;
      end;
    
      Result := Notifications;
    end;
    

    i also had to change anywhere that was calling the

    Notifications := TStringList.Create;
    NotificationsStr := FPreference.getString(TJNotificationAlarm.JavaClass.SETTINGS_NOTIFICATION_IDS, nil);
    Notifications.Text := JStringToString(NotificationsStr);
    

    to the new GetAllNotificationsNamesNonFiltered

    have a look at the source code if something above doesn't make sense Source code here