DEPLHI - Попытка получить данные EXIF ​​для изображений библиотеки в Android - PullRequest
0 голосов
/ 10 февраля 2020

Я новичок в разработке для Android, но не так уж нов в разработке Delphi. В любом случае, я изо всех сил пытаюсь получить данные EXIT из изображения (загруженного из библиотеки) и показать это изображение в форме.

Вот мой код:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  System.Messaging,
  {$IF CompilerVersion > 32}
  System.Permissions,
  {$ENDIF}
  Androidapi.JNI.JavaTypes, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics,
  FMX.Dialogs, FMX.StdActns, FMX.Controls.Presentation, FMX.Objects, FMX.Layouts,
  FMX.ScrollBox, FMX.Memo, FMX.Surfaces, FMX.ExtCtrls, FMX.StdCtrls,
  FMX.Helpers.Android, System.Actions, FMX.ActnList, FMX.MediaLibrary.Actions;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Layout1: TLayout;
    Memo1: TMemo;
    img1: TImageControl;
    procedure Button1Click(Sender: TObject);
  private
    FFileName: JString;
    procedure GetEXIF(const AFileName: JInputStream);
    procedure ResultNotificationMessageHandler(const Sender: TObject; const M: TMessage);
    procedure TakePhoto;
    {$IF CompilerVersion > 32}
    procedure TakePhotoPermissionsResultHandler(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
   {$ENDIF}
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  System.IOUtils, Androidapi.Helpers, Androidapi.JNI.Media, Androidapi.JNIBridge,
  Androidapi.JNI.Provider, Androidapi.JNI.App, Androidapi.JNI.Os,
  Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.Net,
  FMX.Platform.Android, DW.Androidapi.JNI.Os;

const
  cPermissionReadExternalStorage = 'android.permission.READ_EXTERNAL_STORAGE';
  cPermissionWriteExternalStorage = 'android.permission.WRITE_EXTERNAL_STORAGE';
  cPermissionCamera = 'android.permission.CAMERA';

{$IF CompilerVersion > 32}
type
  TGrantResults = TArray<TPermissionStatus>;

  TGrantResultsHelper = record helper for TGrantResults
  public
    function AreAllGranted: Boolean;
  end;

{ TGrantResultsHelper }

function TGrantResultsHelper.AreAllGranted: Boolean;
var
  LStatus: TPermissionStatus;
begin
  for LStatus in Self do
  begin
    if LStatus <> TPermissionStatus.Granted then
      Exit(False); // <======
  end;
  Result := True;
end;
{$ENDIF}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
{$IF CompilerVersion > 32}
  TPermissionsService.DefaultService.RequestPermissions([cPermissionReadExternalStorage, cPermissionWriteExternalStorage, cPermissionCamera], TakePhotoPermissionsResultHandler);
{$ELSE}
  TakePhoto;
{$ENDIF}
end;

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  TMessageManager.DefaultManager.SubscribeToMessage(TMessageResultNotification, ResultNotificationMessageHandler);
end;

destructor TForm1.Destroy;
begin
  TMessageManager.DefaultManager.Unsubscribe(TMessageResultNotification, ResultNotificationMessageHandler);
  inherited;
end;

procedure TForm1.GetEXIF(const AFileName: JInputStream);
var
  LEXIF: JExifInterface;
  LLatLong: TJavaArray<Single>;
  LStream: JFileInputStream;
begin
  try
    LEXIF := TJExifInterface.JavaClass.init(AFileName);
    Memo1.Lines.Clear;
    Memo1.Lines.Add('Date Taken: ' + JStringToString(LEXIF.getAttribute(TJExifInterface.JavaClass.TAG_DATETIME)));
    Memo1.Lines.Add('Camera Make: ' + JStringToString(LEXIF.getAttribute(TJExifInterface.JavaClass.TAG_MAKE)));
    Memo1.Lines.Add('Camera Model: ' + JStringToString(LEXIF.getAttribute(TJExifInterface.JavaClass.TAG_MODEL)));
    LLatLong := TJavaArray<Single>.Create(2);
    try
      if LEXIF.getLatLong(LLatLong) then
      begin
        Memo1.Lines.Add('Latitude: ' + LLatLong.Items[0].ToString);
        Memo1.Lines.Add('Longitude: ' + LLatLong.Items[1].ToString);
      end;
    finally
      LLatLong.Free;
    end;
  except
    on E: Exception do
      ShowMessage(e.Message);
  end;

end;

procedure TForm1.ResultNotificationMessageHandler(const Sender: TObject; const M: TMessage);
var
  LMessage: TMessageResultNotification;
  Str: string;
  FullPhotoUri: Jnet_Uri;
  ms: TMemoryStream;
  jis: JInputStream;
  b: TJavaArray<Byte>;
  NativeBitmap: JBitmap;
  Bitmap: TBitmapSurface;
begin
  if M is TMessageResultNotification then
  begin
    LMessage := TMessageResultNotification(M);
    if LMessage.RequestCode = 10011 then
      if (LMessage.ResultCode = TJActivity.JavaClass.RESULT_OK) then
        if Assigned(LMessage.Value) then
        try
          try
            FullPhotoUri := LMessage.Value.getData();

            jis := TAndroidHelper.Context.getContentResolver.openInputStream(FullPhotoUri);

            GetEXIF(jis);

            ms := TMemoryStream.Create;
            b := TJavaArray<Byte>.Create(jis.available);
            jis.read(b);
            ms.Write(b.Data^, b.Length);

            img1.Bitmap.LoadFromStream(ms);

            jis.close;

          except
            on E: Exception do
              Application.ShowException(e);
          end;
        finally
          ms.Free;
        end;

  end;
end;

procedure TForm1.TakePhotoPermissionsResultHandler(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
begin
  if TGrantResults(AGrantResults).AreAllGranted then
    TakePhoto
  else
    ShowMessage('Not all photo permissions granted!');
end;


// Based on: https://developer.android.com/training/camera/photobasics#java
procedure TForm1.TakePhoto;
var
  LIntent: JIntent;
  LFile, LDir: JFile;
  LUri: Jnet_Uri;
  LFileName: string;
begin
  LIntent := TJIntent.Create;
  LIntent.setAction(TJIntent.JavaClass.ACTION_OPEN_DOCUMENT).addCategory(TJIntent.JavaClass.CATEGORY_OPENABLE).setType(StringToJString('image/*'));

  if LIntent.resolveActivity(TAndroidHelper.Context.getPackageManager) <> nil then
  begin
    MainActivity.startActivityForResult(LIntent, 10011);
  end
  else
    ShowMessage('Cannot take a photo!');

end;

end.

Теперь ошибка исходит от строка:

img1.Bitmap.LoadFromStream(ms);

и ошибка:

Project ObtainPhotoInfoDemo.apk поднял класс исключения Ошибка сегментации (11).

Спасибо

ОБНОВЛЕНИЕ

НАЙТИ РЕШЕНИЕ !!!!

blackapps прокомментировал и дал мне идею поток должен быть закрыт и снова открыт для использования в другом вызове, подобном этому:

    FullPhotoUri := LMessage.Value.getData();
    //get input stream
    jis := TAndroidHelper.Context.getContentResolver.openInputStream(FullPhotoUri);
    GetEXIF(jis);
    //have to close it because GetEXIF already consumed it
    jis.close;
    //open it again
    jis := TAndroidHelper.Context.getContentResolver.openInputStream(FullPhotoUri);

    NativeBitmap := TJBitmapFactory.JavaClass.decodeStream(jis);
    Surf := TBitmapSurface.Create;
    if JBitmapToSurface(NativeBitmap, Surf) then
      img1.Bitmap.Assign(Surf);

    jis.close;
...