Firemonkey blocking dialogs on Android

2019-07-29 06:42发布

问题:

I am porting an older project (app for iOS ans Android) for a customer from an older AppMethod version to the newest RAD Studio version (10.0 Berlin). MessageDlg and similar were used often in the project but this does not work anymore. The apps shows a message, that modal dialogs are not supported on Android. I know and understand why this is so for the Android platform (so please don't mark this question as a dublicate by referencing to an explanation - I'm asking for something else!).

Firemonkey allows to set an anonymous function to be executed asynchronously after the user taps on a button in the dialog box or when it is closed. However, there is often code which depends on users decision or code which must be executed after the dialog box, regardless of which button the user taps on. For example, a dialog box must ask the user for his decision in the middle of an ongoing operation. Further operations then depend on the users decision (which might also include to stop further code execution in the current application!). Since RAD Studio does not support blocking dialogs on Android, I seem to be forced to break up my code - from one function to multiple functions (worse readability...). This gets even more complicated when there are nested function calls with possible modal dialog boxes, which require user interaction.

Is there still a way to emulate blocking dialogs somehow? It doesn't has to be perfect. I just don't want to rewrite a lot of code and outsource even small code fragments into many separate functions, everwhere where user interaction is required.


OK, I think the following "dirty" solution might be sufficient for me:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.DialogService,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
  blockDlg: array[0..99] of Boolean;
  blockDlgIdx: Integer = 0;

implementation

{$R *.fmx}

procedure NShowMessageAsync(AMessage: String);
begin
  TDialogService.ShowMessage(AMessage);
end;

procedure NShowMessageSync(AMessage: String);
var
  locBlockIdx: Integer;
begin
  if blockDlgIdx = Length(blockDlg)-1 then blockDlgIdx := 0 else Inc(blockDlgIdx);
  locBlockIdx := blockDlgIdx;

  blockDlg[locBlockIdx] := true;
  TDialogService.ShowMessage(AMessage,
    procedure(const AResult: TModalResult)
    begin
      blockDlg[locBlockIdx] := false;
    end);
  while blockDlg[locBlockIdx] do begin
    Sleep(40);
    Application.ProcessMessages();
  end;
end;

procedure NMessageDialogAsync(const AMessage: string; const ADialogType: TMsgDlgType;
  const AButtons: TMsgDlgButtons; const ADefaultButton: TMsgDlgBtn;
  const AHelpCtx: LongInt);
begin
  TDialogService.MessageDialog(AMessage, ADialogType, AButtons, ADefaultButton,
    AHelpCtx, procedure(const AResult: TModalResult) begin end);
end;

function NMessageDialogSync(const AMessage: string; const ADialogType: TMsgDlgType;
  const AButtons: TMsgDlgButtons; const ADefaultButton: TMsgDlgBtn;
  const AHelpCtx: LongInt): TModalResult;
var
  locBlockIdx: Integer;
  LModalResult: TModalResult;
begin
  Result := -1;
  if blockDlgIdx = Length(blockDlg)-1 then blockDlgIdx := 0 else Inc(blockDlgIdx);
  locBlockIdx := blockDlgIdx;

  blockDlg[locBlockIdx] := true;
  TDialogService.MessageDialog(AMessage, ADialogType, AButtons, ADefaultButton, AHelpCtx,
    procedure(const AResult: TModalResult)
    begin
      LModalResult := AResult;
      blockDlg[locBlockIdx] := false;
    end);

  while blockDlg[locBlockIdx] do begin
    Sleep(40);
    Application.ProcessMessages();
  end;

  Result := LModalResult;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  mr: TModalResult;
begin
  mr := NMessageDialogSync('1',
    System.UITypes.TMsgDlgType.mtInformation,
    [System.UITypes.TMsgDlgBtn.mbYes, System.UITypes.TMsgDlgBtn.mbNo, System.UITypes.TMsgDlgBtn.mbCancel],
    System.UITypes.TMsgDlgBtn.mbYes,
    0);
  NShowMessageSync(IntToStr(mr));
  mr := NMessageDialogSync('2',
    System.UITypes.TMsgDlgType.mtWarning,
    [System.UITypes.TMsgDlgBtn.mbYesToAll, System.UITypes.TMsgDlgBtn.mbAbort],
    System.UITypes.TMsgDlgBtn.mbAbort,
    0);
  NShowMessageSync(IntToStr(mr));
  mr := NMessageDialogSync('3',
    System.UITypes.TMsgDlgType.mtInformation,
    [System.UITypes.TMsgDlgBtn.mbIgnore, System.UITypes.TMsgDlgBtn.mbAll, System.UITypes.TMsgDlgBtn.mbHelp, System.UITypes.TMsgDlgBtn.mbClose],
    System.UITypes.TMsgDlgBtn.mbClose,
    0);
  NShowMessageSync(IntToStr(mr));

  Form1.Fill.Kind := TBrushKind.Solid;
  if Form1.Fill.Color = TAlphaColors.Red then
    Form1.Fill.Color := TAlphaColors.Blue
  else
    Form1.Fill.Color := TAlphaColors.Red;
end;

end.

Not very clean, but at least I can use this for a while.