Einzelnen Beitrag anzeigen

Renate Schaaf

Registriert seit: 25. Jun 2020
Ort: Lippe
114 Beiträge
 
Delphi 11 Alexandria
 
#1

Versuch: TFileBrowser für Android, geht das ohne globale Variable?

  Alt 12. Nov 2020, 19:32
Ich habe versucht, einen Ersatz für TFileOpenDialog zu bauen, der möglichst einfach für Android aufrufbar ist. Das geht aber nur, indem der Benutzer eine Callback-Funktion definiert, also ein "OnExecute", jedenfalls fällt mir nichts anderes ein. Damit der Aufruf möglichst zusammenhängend ist, kann man das Callback anonym definieren.

Hier ist ein Beispiel für den Aufruf:

Delphi-Quellcode:
procedure TForm1.BrowseForVideoFile;
begin
{$IFDEF ANDROID}
  TFileBrowser.BrowseForFile(ctVideo,
  // This procedure is called when the user has picked an item
    procedure(var filename: string; var success: boolean)
    begin
      if success then
      begin
        fVideoFilename := filename;
        Edit2.text := fVideoFilename;
        ProcessVideoFile;
      end
      else
        TDialogService.ShowMessage
          ('Pick the file by opening the Documents folder. Don''t use any of the other apps.');
    end);

{$ELSE}
...
end;
Dafür ist TFileBrowser.BrowseForFile eine class procedure. Bisher habe ich das nur ans Laufen bekommen, indem ich im Interface eine globale Variable vom Typ TFileBrowser definiere. Kann man das irgendwie schlauer machen?

Hier ist der source code für TFileBrowser, ist nicht so lang:

Delphi-Quellcode:
unit UAndroidTools;

interface

uses
  System.Messaging,
  AndroidAPI.Helpers,
  AndroidAPI.Jni.Os,
  AndroidAPI.Jni.GraphicsContentViewText,
  AndroidAPI.Jni.Net,
  FMX.Platform.Android,
  AndroidAPI.JNIBridge,
  AndroidAPI.Jni.JavaTypes;

type
  TContentEnum = (ctVideo, ctImages, ctAudio, ctText, ctAll);

  TFileBrowseEvent = reference to procedure(var Filename: string;
    var Success: boolean);

  TFileBrowser = class
  private
    fOnExecute: TFileBrowseEvent;
    fFilename: string;
    function HandleIntentAction(const Data: JIntent): boolean;
    procedure DoExecute(Success: boolean);
  protected
    fMessageSubscriptionID: integer;
    procedure HandleActivityMessage(const sender: TObject; const M: TMessage);
  public
    ///<summary> Use Android API to have the user pick a file with a certain content type </summary>
    /// <param OnExecute> Event occuring when the user has picked a file. Write a (anonymous) procedure(var Filename: string; var Success: boolean) to handle the event. <param>
    class procedure BrowseForFile(Content: TContentEnum;
      OnExecute: TFileBrowseEvent);
  end;

implementation


const
  ContentStrings: array [TContentEnum] of string = ('video/*', 'image/*',
    'audio/*', 'text/*', '*/*');

  { TFileBrowser }

var
  //Wie schafft man das ohne globale Variable?
  _TheFileBrowser: TFileBrowser;

class procedure TFileBrowser.BrowseForFile(Content: TContentEnum;
  OnExecute: TFileBrowseEvent);
var
  Intent: JIntent;
begin
  _TheFileBrowser := TFileBrowser.Create;
  //Das ist doch irgendwie doof.
  With _TheFileBrowser do
  begin
    fOnExecute := OnExecute;
    fMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage
      (TMessageResultNotification, HandleActivityMessage);
    Intent := TJIntent.Create;
    Intent.setType(StringToJString(ContentStrings[Content]));
    Intent.setAction(TJIntent.JavaClass.ACTION_GET_CONTENT);
    MainActivity.startActivityForResult(Intent, 0);
  end;
end;

procedure TFileBrowser.DoExecute(Success: boolean);
begin
  if assigned(fOnExecute) then
    fOnExecute(fFilename, Success);
  self.disposeof;
end;

procedure TFileBrowser.HandleActivityMessage(const sender: TObject;
  const M: TMessage);
begin
  if M is TMessageResultNotification then
  begin
    DoExecute(HandleIntentAction(TMessageReceivedNotification(M).Value));
  end;
end;

function TFileBrowser.HandleIntentAction(const Data: JIntent): boolean;
var
  C: JCursor;
  I: integer;
begin
  C := MainActivity.getContentResolver.query(Data.getData, nil,
    StringToJString(''),
    nil, StringToJString(''));

  C.moveToFirst;
  Result := false;
  for I := 0 to C.getColumnCount - 1 do
  begin
    if JStringToString(C.getColumnName(I)) = '_datathen
    // '_data' column contains the path
    begin
      fFilename := JStringToString(C.getString(I));
      Result := true;
      Break;
    end;
  end;
  if not Result then
    fFilename := '';
end;

end.
Gruß, Renate
Renate
  Mit Zitat antworten Zitat