DEPLHI - Trying to get EXIF data on library images in Android

浪尽此生 提交于 2020-03-05 00:33:20

问题


I am new at developing for Android, but not so new on Delphi development though. Anyway I am struggling to get EXIT data from an image (loaded from library) and show that image on the form.

Here is my code:

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.

Now, the error comes from the line:

img1.Bitmap.LoadFromStream(ms);

and the error is:

Project ObtainPhotoInfoDemo.apk raised exception class Segmentation fault (11).

Thanks

UPDATE

FOUND THE SOLUTION!!!!

blackapps commented and gave me an idea, the stream should be closed and reopened again to be used in an another call like this:

    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;

来源:https://stackoverflow.com/questions/60155948/deplhi-trying-to-get-exif-data-on-library-images-in-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!