Перейти к содержанию
Fire Monkey от А до Я

Freezer_86

Пользователи
  • Постов

    8
  • Зарегистрирован

  • Посещение

Сообщения, опубликованные Freezer_86

  1. Доброго времени суток. Столкнулся с такой же проблемой в 10.4.2. В стандартном примере все работает, а в мигрированном приложении из 10.3.3 - приложение всегда ловит отмену запроса на фото. Я так понимаю где-то не хватает прав для временного файла. 

    Путем долгих и мучительных экспериментов и курением доки организовал эту всю штуку через нативные интенты.

    Код вырезан из рабочего проекта, по этому извините если что-то не так. Но смысл должен быть понятен.

    Здесь кроме самого фото еще реализовано автоматическое поворачивание картинки по Exif-даным, относительно того в каком положении был телефон.

    Так же очень важно не забыть выставить права в опциях приложение и подключить Secure File Sharing в Entitlement List. С Uses секцией надеюсь разберетесь, не уверен что нужно для Вашей задачи все из того что я кинул.

    Uses 
      ....
        Androidapi.JNI.GraphicsContentViewText,
      Androidapi.JNI.App,
      Androidapi.JNI.JavaTypes,
      Androidapi.Helpers,
      FMX.Platform.Android,
      System.Permissions,
      Androidapi.JNI.Os,
      Androidapi.JNI.Net,
      Androidapi.JNI.Support,
      Androidapi.JNI.Provider,
      Androidapi.JNIBridge,
      FMX.Helpers.Android,
      FMX.Surfaces,
      Androidapi.JNI.Media;
    
      TfmMain = class(TForm)
         ...
        procedure FormCreate(Sender: TObject);
        procedure btnPhotos_ShowCameraClick(Sender: TObject);
        ...
      protected
        FMessageSubscriptionID : integer;
        FPermissionWriteExternalStorage: string;
        FPermissionCamera: string;
        FPermissionReadExternalStorage: string;
    
        FphotoUri :Jnet_Uri;
        JFileName: JFile;
    	
        procedure DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc);
        procedure TakePicturePermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
        procedure TakePicturePermissionRequestAfterINitResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
    
        procedure HandleActivityMessage(const Sender: TObject; const M: TMessage);
        function OnActivityResult(RequestCode, ResultCode: Integer; Data: JIntent): Boolean;
      end;
    
    procedure TfmMain.FormCreate(Sender: TObject);
    begin
      FPermissionCamera := JStringToString(TJManifest_permission.JavaClass.CAMERA);
      FPermissionReadExternalStorage := JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE);
      FPermissionWriteExternalStorage := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);
      ...
    end;
    
    procedure TfmMain.HandleActivityMessage(const Sender: TObject; const M: TMessage);
    begin
      if M is TMessageResultNotification then begin
        OnActivityResult(TMessageResultNotification(M).RequestCode, TMessageResultNotification(M).ResultCode, TMessageResultNotification(M).Value);
      end;
    end;
    
    function TfmMain.OnActivityResult(RequestCode, ResultCode: Integer;  Data: JIntent): Boolean;
      procedure SetPhotoData();
      var
        jBmp : JBitmap;
        xSurface : TBitmapSurface;
    
        vImage : TImage;
    
        xExif : JExifInterface;
        sOrientation : JString;
        rotationAngle, orientation : integer;
        sFile : string;
    
        ScreenService: IFMXScreenService;
      begin
        if RequestCode = 1001 then begin
          if ResultCode = TJActivity.JavaClass.RESULT_OK then begin
            if FphotoUri <> nil then begin
              jBmp := TJImages_Media.JavaClass.getBitmap(SharedActivity.getContentResolver, FphotoUri);
              if jBmp <> nil then begin
                xSurface := TBitmapSurface.Create;
                try
                  JBitmapToSurface(jBmp, xSurface);
                  vImage := TImage.Create(nil);
    
                  try
                    vImage.Bitmap.Assign(xSurface);
    
                    sFile := JStringToString(JFileName.getAbsolutePath);
                    xExif := TJExifInterface.JavaClass.init(StringToJString(sFile));
    
                    sOrientation := xExif.getAttribute(TJExifInterface.JavaClass.TAG_ORIENTATION);
                    if sOrientation = nil then
                      orientation := TJExifInterface.JavaClass.ORIENTATION_NORMAL
                    else
                      orientation := StrToIntDef(JStringToString(sOrientation), 0);
    
                    rotationAngle := 0;
    
                    if orientation = TJExifInterface.JavaClass.ORIENTATION_ROTATE_90 then
                      rotationAngle := 90;
                    if orientation = TJExifInterface.JavaClass.ORIENTATION_ROTATE_180 then
                      rotationAngle := 180;
                    if orientation = TJExifInterface.JavaClass.ORIENTATION_ROTATE_270 then
                      rotationAngle := 270;
    
                    if rotationAngle <> 0 then
                     vImage.Bitmap.Rotate(rotationAngle);
    
                    //Готовое фото уже в vImage - выводим куда нужно
                    imPhotos_Photo.Bitmap.Assign(vImage.Bitmap);
                  finally
                   vImage.Free;
                  end;
                finally
                  xSurface.Free
                end{try..finally};
              end else begin
                //не вдалось завантажити картинку
                TDialogService.ShowMessage('Не вдалось завантажити картинку');
              end{if..else};
            end else begin
              //пустий Uri
              TDialogService.ShowMessage('Пустий Uri');
            end{if..else};
          end else begin
            TDialogService.ShowMessage('Відмовились робити фото');
          end{if..else};
        end{if};
      end;
    
    begin
      Result := False;
    
      TMessageManager.DefaultManager.Unsubscribe(TMessageResultNotification, FMessageSubscriptionID);
      FMessageSubscriptionID := 0;
    
      if RequestCode = 1001 then begin
    	  // робота з камерою - отримання фото
        SetPhotoData();
    
    	exit;
      end;
      ...
    end;
    
    
    procedure TfmMain.TakePicturePermissionRequestResult(Sender: TObject;
      const APermissions: TArray<string>;
      const AGrantResults: TArray<TPermissionStatus>);
    var
      intent : jintent;
      Authority: JString;
    begin
      // 3 permissions involved: CAMERA, READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE
      if (Length(AGrantResults) = 3) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) and (AGrantResults[2] = TPermissionStatus.Granted) then begin
        //створюємо "намір" отримання фото з камери
        intent := TJIntent.JavaClass.init;
        intent.setAction(TJMediaStore.JavaClass.ACTION_IMAGE_CAPTURE);
    
        //створюємо тимчасовий файл
        JFileName := TJFile.JavaClass.createTempFile(StringToJString('picture'), StringToJString('.jpg'));
        //отримуємо опис прав по файлам
        Authority := StringToJString(JStringToString(TAndroidHelper.Context.getApplicationContext.getPackageName) + '.fileprovider');
        //отримуємо URI-до тимчасового файлу в контексті нашої аплікації
        FphotoUri:= TJFileProvider.JavaClass.getUriForFile(TAndroidHelper.Context, Authority, JFileName);
        //кажемо що наш "намір" має писати дані в цей тимчасовий файл
        intent.putExtra(TJMediaStore.JavaClass.EXTRA_OUTPUT,
                        TJParcelable.Wrap((FphotoUri as ILocalObject).GetObjectID)
                       );
        //реєструємось на отримання подій
        FMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage(TMessageResultNotification, HandleActivityMessage);
        //стартуємо наш "намір"
        MainActivity.startActivityForResult(intent, 1001);
      end else
        TDialogService.ShowMessage('Не надані необхідні для роботи дозволи!');
    end;
    
    procedure TfmMain.DisplayRationale(Sender: TObject;
      const APermissions: TArray<string>; const APostRationaleProc: TProc);
    var
      I: Integer;
      RationaleMsg: string;
    begin
      for I := 0 to High(APermissions) do
      begin
        if APermissions[I] = FPermissionCamera then
          RationaleMsg := RationaleMsg + 'The app needs to access the camera to take a photo' + SLineBreak + SLineBreak
        else if APermissions[I] = FPermissionReadExternalStorage then
          RationaleMsg := RationaleMsg + 'The app needs to read a photo file from your device';
      end;
    
      // Show an explanation to the user *asynchronously* - don't block this thread waiting for the user's response!
      // After the user sees the explanation, invoke the post-rationale routine to request the permissions
      TDialogService.ShowMessage(RationaleMsg,
        procedure(const AResult: TModalResult)
        begin
          APostRationaleProc;
        end);
    end;
    
    procedure TfmMain.btnPhotos_ShowCameraClick(Sender: TObject);
    begin
      PermissionsService.RequestPermissions( [FPermissionCamera, FPermissionReadExternalStorage, FPermissionWriteExternalStorage],
                                             TakePicturePermissionRequestResult,
                                             DisplayRationale
                                           );
    end;

    P.S. возможно нужно будет вставить в манифест:

      android:requestLegacyExternalStorage="true"

     

  2. Это не очень относиться к теме но оставлю это здесь, вдруг кому пригодиться. В моей задаче нужно было еще отслеживать на какой странице находиться пользователь, а как оказалось свойство URL тоже работает некорректно. Пришлось "накостылять" следующее:

    В WebBrowser.Android.pas добавляем:

    function TAndroidWebBrowserService.originalUrl: string;
    begin
      if FJWebBrowser = nil then
        Result := FURL
      else
        Result := JStringToString(FJWebBrowser.getOriginalUrl);
    end;

    В WebBrowser.Win.pas добавляем:

    function TWindowsWebBrowserService.LocationUrl: string;
    begin
      Result := FUrl;
      if FInstance <> nil then
        Result := FInstance.LocationURL;
    end;

    В WebBrowser.pas изменим: 

    function TKCustomWebBrowser.GetURL: string;
    begin
      if (csDesigning in ComponentState) or (FWeb = nil) then
        Result := FURL
      else
        {$IFDEF MSWINDOWS}
        Result := (FWeb as TWinWBMediator).WB.LocationUrl;
        {$ELSE}
        Result := (FWeb as TAndroidWebBrowserService).originalUrl
        {$ENDIF}
    end;

    Как результат - корректная работа панели навигации.

  3. Натыкался на такую проблему, смог обойти написанием своей копией TWebBrowser. Основное изменение для обхода именно ошибки с CoboBox'ом это в начале процедуры TCustomWebBrowser.FormHandleCreated вставкой кода:

      {$IFDEF MSWINDOWS}
       exit;
      {$ENDIF}

    После этого никаких проблем с пересозданием. Правда у меня все браузера создаются в Runtime. Не знаю или это подойдет для нормальной работы с загрузкой из DFM.

  4. Как я вижу есть несколько возможных причин: неверный формат данных, неверная реализация TGrid под Android, неподдерживаемая комбинация компонентов, ошибки в моем коде реализации (что маловероятно поскольку кода почти нет, Live Binding).

    Неужели никто не сталкивался с подобными проблемами?

  5. Пишу кроссплатформенное приложение. Результат поиска отображается в TGrid. Стал вопрос отображения картинки в одной из колонок.

    На Windows все ок, но на планшете происходят просто чудеса: при первом отображение все корректно, но если простоколить вверх-вниз как картинки одни перетираются другими, часть вообще отображается вверх ногами.

    Код для сохранение картинки(jpg) в базу:

    if Assigned(sm) then begin
      sm.Position := 0;
      //TBlobField(dmData.cdsPlayerData.FieldByName('Photo')).LoadFromStream(sm);
    
      vImage := TImage.Create(nil);
    
      try
        sm.Position := 0;
        vImage.Bitmap.LoadFromStream(sm);
    
        vKoef := vImage.Bitmap.Height / 64;
    
        vImage.Bitmap.Resize(Trunc(vImage.Bitmap.Width / vKoef), Trunc(vImage.Bitmap.Height / vKoef));
    
        sm.Free;
        sm := TMemoryStream.Create();
    
        try
          vImage.Bitmap.SaveToStream(sm);
          TBlobField(dmData.cdsPlayerData.FieldByName('SmallPhoto')).LoadFromStream(sm);
        finally
          sm.Free;
        end;
      finally
        vImage.Free;
      end;
    
    end{if};

    До скрола:

    Screenshot_20170725-183545.thumb.png.4698c66d810bf6345198966e41784b6f.png

    После скрола:

    Screenshot_20170725-183501.thumb.png.c861639ace51cb6ac8d2d03ebfffc9ea.png

    Пробовал и LiveBinding, и ручную прорисовку - результат один и тот же. Есть идеи что не так?

    P.S. Знаю что нужно делать через TListView, но заказчик хочет «сеточку как в старой программе», так как на android будет работать только на планшетах – я согласился.

     

×
×
  • Создать...