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

Фотография стандартной камерой смартфона


Pax Beach

Вопрос

В Android 6 на Berlin 10.1.1 перестала работать стандартная функция TakePhotoFromCameraAction.

2016-12-23_09-43-57.png

Иногда работает, иногда нет.

Поэтому я решил написать нативную замену этой функции. За основу взят пример с портала разработчиков Android.

Мой пример про видео на базе отправки Intetn смартфону и получение результата в виде другого Intent, отлично работает.

Но для фотографий этот пример сыпется, после выполнения фотографирования система возвращается в мое приложение и оно вылетает с ошибкой:

Цитата

12-22 22:13:10.094: E/AndroidRuntime(31796): FATAL EXCEPTION: main

12-22 22:13:10.094: E/AndroidRuntime(31796): Process: com.embarcadero.TakePhotoIntent, PID: 31796

12-22 22:13:10.094: E/AndroidRuntime(31796): java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=null} to activity {com.embarcadero.TakePhotoIntent/com.embarcadero.firemonkey.FMXNativeActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.embarcadero.firemonkey.medialibrary.MediaImage.isFileExists()' on a null object reference
 

Если заглянуть в исходнки, там все обвязано сообщениями в лог монитора. Последнее сообщение «+ Intent is sent».

Помогите пожалуйста разобраться, в чем может быть проблема?

 

PhotoIntent.zip

Ссылка на комментарий

Рекомендуемые сообщения

  • 1

Проблема решалась очень просто.

Оказывается нужно при вызове startActivityForResult использовать requestCode отличный от занятых в FMX, я использую REQUEST_IMAGE_CAPTURE = 1001 тогда обработчик сообщений FMX поймет, что сообщение не для него и передаст его в систему. 
 

 

Ссылка на комментарий
  • 0
В 01.03.2017 в 00:28, AlexShaman сказал:

Что-то у меня не получается.

Такая же ошибка, как у вас, 

добавил PhotoIntent.setFlags(TJIntent.javaclass.FLAG_ACTIVITY_NEW_TASK ); ошибка ушла, но не срабатывает callback

Какая версия Delphi, Andorid?

REQUEST_IMAGE_CAPTURE  поменяли?

Ссылка на комментарий
  • 0
1 час назад, ENRGY сказал:

Спасибо большое за пример. 

Вопрос, как сделать чтобы фото не добавлялось в Library?

Читая примеры гугла, я так понял, что придется лезть в папку library и удалять последнюю фотку, если это сильно нужно.

 

Ссылка на комментарий
  • 0

В итоге, я пересел обратно на съемку фото через TAction.

В Берлине 10.1.2 для Android 6+ делаю запрос прав на камеру и доступ к внешнему хранилищу (камера не обязательно, потому что съемка ведется нативным приложением) — все нормально заработало.

Ссылка на комментарий
  • 1

По поводу удаления из Gallery. Это виртуальная папка, это значит что недостаточно удалить файл из SharedCameraPath - т.к. он может быть в другом месте, т.к. MediaScanner собирает фотки со многих других папок,  но обычно достаточно этого пути.

Но, даже если его удалить оттуда, он все равно останется в Gallery - его нужно удалять из SQL базы андроида.

 

В общем идея следующая (описана тут) - до вызова интента сначала прочитать последний Image ID (ContentResolver.Query ) который лежит там, сохранить эту цифру.

Затем после вызова интента камеры, запросить все id что больше сохраненного, как правило там должен появится один файл (одна запись с ID больше сохраненного) - и удалить его через ContentResolver.Delete. Параллельно можно сразу скопировать этот готовый jpeg файл, до удаления, чтобы не тратить время на создание своего из битмапа, т.к. можно получит и путь к нему.

 

 

Изменено пользователем ENRGY
Ссылка на комментарий
  • 0

Сделал решение для описанного выше варианта.

Добавил его на Stackoverflow

 

unit Misc.Android;

interface

uses
  SysUtils,
  Androidapi.JNI.GraphicsContentViewText, Androidapi.Helpers, Androidapi.JNI.JavaTypes,
  Androidapi.JNIBridge, Androidapi.JNI.Provider;

type
  TGallery = class
  public
    class function GetLastImageID: integer;
    class function GetNextImageIDFromID(aFromID: integer; out aImagePath: string): integer;
    class function DeleteImageByID(aID: integer): boolean;
  end;


implementation

const
  _ID = '_id'; //  TJBaseColumns.JavaClass._ID   // uri in Androidapi.JNI.Provider

{ TGallery }

{If you're using action TakePhotoFromCameraAction remember to set NeedSaveToAlbum to true.
 It does not work, because of Android problems, but it can work in future.}

class function TGallery.GetLastImageID: integer;
var
  vContent: JContentResolver;
  vValues: TJavaObjectArray<JString>;
  vOrderBy: JString;
  vCursor: JCursor;
begin
  Result := -1;
  vContent := TAndroidHelper.Activity.getContentResolver;

  vValues := TJavaObjectArray<JString>.Create(1);
  vValues[0] := TJBaseColumns.JavaClass._ID;

  vOrderBy := StringToJString(_ID + ' DESC');

  vCursor := vContent.query(TJImages_Media.JavaClass.EXTERNAL_CONTENT_URI,
      vValues, nil, nil, vOrderBy);
  try
    if vCursor.moveToFirst then
      Result := vCursor.getInt( vCursor.getColumnIndex(TJBaseColumns.JavaClass._ID) );
  finally
    vCursor.close;
  end
end;

// Result is next Image ID and its aImagePath - is path to jpg image
class function TGallery.GetNextImageIDFromID(aFromID: integer; out aImagePath: string): integer;
var
  vContent: JContentResolver;
  vValues: TJavaObjectArray<JString>;
  vFilter: JString;
  vOrderBy: JString;
  vArgs : TJavaObjectArray<JString>;
  vCursor: JCursor;
begin
  Result := -1;
  aImagePath := '';
  vContent := TAndroidHelper.Activity.getContentResolver;
  vValues := TJavaObjectArray<JString>.Create(2);
  vValues[0] := TJMediaStore_MediaColumns.JavaClass.DATA;
  vValues[1] := TJBaseColumns.JavaClass._ID;
   // vValues[1] := TJMediaStore_MediaColumns.JavaClass.SIZE;
   // vValues[1] := TJImages_ImageColumns.JavaClass.DATE_TAKEN;

  vOrderBy := StringToJString(_ID + ' DESC');
  vFilter := StringToJString(_ID + '>?');
  vArgs := TJavaObjectArray<JString>.Create(1);  
  vArgs[0] := StringToJString(aFromID.ToString);

  vCursor := vContent.query(TJImages_Media.JavaClass.EXTERNAL_CONTENT_URI,
     vValues, vFilter, vArgs, vOrderBy);
  try
    if (vCursor.getCount > 0) and vCursor.moveToFirst then
    begin
      Result := vCursor.getInt( vCursor.getColumnIndex(TJBaseColumns.JavaClass._ID) );
      // vCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN));
      //vSize := wCursor.getLong(wCursor.getColumnIndex(TJMediaStore_MediaColumns.JavaClass.SIZE));
      aImagePath := JStringToString(vCursor.getString(
          vCursor.getColumnIndex(TJMediaStore_MediaColumns.JavaClass.DATA) ));
    end;
  finally
    vCursor.close;
  end;
end;

class function TGallery.DeleteImageByID(aID: integer): boolean;
var
  vContent: JContentResolver;
begin
  vContent := TAndroidHelper.Activity.getContentResolver;
  Result := vContent.delete(TJImages_Media.JavaClass.EXTERNAL_CONTENT_URI,
        StringToJString(_ID + '=' + aID.ToString), nil) = 1;
end;

end.
Изменено пользователем ENERGY
Ссылка на комментарий
  • 0
В 24.03.2017 в 23:27, ENRGY сказал:

Pax Beach

А до этого не было что-ли прав? 

 

За ответ — зачет, плюсанул на стэке.

А права прописаны в Permissions проекта, но для  Android 6.0 и выше запрашиваю еще и дополнительно.

 

Ссылка на комментарий
  • 0
  • Модераторы
5 часов назад, ENRGY сказал:

Pax Beach

Т.е. для шестого Android нужно запрашивать права на камеру дополнительно? Даже если они уже указаны в Permissionss?

если используется SDK > 23, то приложение должно запросить права у пользователя.

Но стандартный SDK студии < 23 так что достаточно использовать старый способ (указать в настройках проекта)

Ссылка на комментарий
  • 0
В 25.3.2017 в 21:26, ENRGY сказал:

Сделал решение для описанного выше варианта.

Добавил его на Stackoverflow

Поставили такую же задачу - удалять оставшиеся фото из галереи.

Взяли за основу твое решение - работает через раз, и только при отладке:

- перед созданием фото получаем id последнего фото (id_last)

- фотографируем

- ищем все id которые больше id_last и удаляем их.

На последнем шаге и получаем фейл - из под работающего приложения никогда не возращаются id больше id_last. Из под отладки - получаем лишь иногда. Причем если в момент отладки переключиться в галерею, то там есть свежие фото.

Есть идеи, в какую сторону копать? Может перед vCursor := vContent.query(...) нужно какой кеш обновить?

Ссылка на комментарий
  • 0
16 минут назад, Barbanel сказал:

Поставили такую же задачу - удалять оставшиеся фото из галереи.

Взяли за основу твое решение - работает через раз, и только при отладке:

- перед созданием фото получаем id последнего фото (id_last)

- фотографируем

- ищем все id которые больше id_last и удаляем их.

На последнем шаге и получаем фейл - из под работающего приложения никогда не возращаются id больше id_last. Из под отладки - получаем лишь иногда. Причем если в момент отладки переключиться в галерею, то там есть свежие фото.

Есть идеи, в какую сторону копать? Может перед vCursor := vContent.query(...) нужно какой кеш обновить?

Сразу приходит в голову разумная пауза перед запросом. Не пробовали?

Ссылка на комментарий
  • 0

До трех секунд пробовали, к тому же нативное приложение галереи обнаруживает новые фото сразу же, при переключении на него.

Создается впечатление что дело не в паузе.

Ссылка на комментарий
  • 0

@Barbanel

Никаких проблем c этим кодом не было с 4 и 5 Android. На 6 не проверял. Точно такой же код пишет народ на Java там тоже никаких проблем по этому поводу не писали.

Чтобы убедиться что проблема точно не в паузе, попробуйте установить sleep на 10- 20 секунд. 

Может вы не закрываете курсор после какого то из запросов раньше?

да еще момент, в Action TakePhotoFromCameraAction нужно установить свойство NeedSaveToAlbum := true  . Возможно это ключевой момент в вашем вопросе (т.к. на некоторых телефонах (привет HTC) фотки сохраняются в галерею даже если NeedSaveToAlbum = false, это уже баг конкретной модификации Андроида).

Изменено пользователем ENRGY
Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

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