Перейти к содержанию

Вопросы

В 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

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

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

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

 

 

Поделиться сообщением


Ссылка на сообщение
  • 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

Pax Beach

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

Поделиться сообщением


Ссылка на сообщение
  • 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 эмодзи.

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

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

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


  • Похожий контент

    • От fredhack
      Доброго времени суток подскажите пожалуйста как мне получить ссылку из браузера запущенного через intent в общем суть такова:
      1 запускаю браузер через интент с сылкой http://fire-monkey.ru
      2 на ссылке происходит редирект
      3 я попадаю на http://fire-monkey.ru/forum/
      Как получить последнюю ссылку после редиректа? в данном случае она равна http://fire-monkey.ru/forum/
      var Intent: JIntent; begin Intent := TJIntent.Create; Intent.setAction(TJIntent.JavaClass.ACTION_VIEW); Intent.setData(StrToJURI('http://fire-monkey.ru')); SharedActivity.startActivity(Intent); end;  
    • От A. Sharif
      Возможно ли с помощью intent получить от какого-либо из официальных приложений Вконтакте/Фейсбук/Инст/Google access-token (через кол-бэк метод)?
      Предусматривается ли данная возможность этими приложениями? Если кто делал - приведите, пожалуйста, пример получения результата. 
    • От Wovan2
      Здравствуйте.
      Пишу на Delphi 7. И все было нормально пока не у нас не появился терминал сбора данных на Androide. На Delphi 10 написал простенькое приложение. Но тут встала проблема с занесением данных в поле ввода со сканера штрихкода. В инете нашел, что это дело просто решается интентами. Вообще в windows я подобное делал: вешал процесс, следящий за com-портом, и при появлении данных на нем пересылал их куда надо. В Android должно быть не сложнее. Но я запутался во всей этой куче параметров и функций, передаваемых и получаемых "намерениями".
      В общем вот что мы имеем на данный момент.
      В настройках сканера прописано:
       
      Intent output - android.intent.ACTION_DECODE_DATA Intent string extra - barcode_string На просторах нашел запуск BroadcastReceiver.
      На событие получения сообщения повесил 
      ed.Text := JStringToString(csIntent.getExtras.getString(TJIntent.JavaClass.EXTRA_INTENT)); где csIntent параметр из  BroadcastReceiverOnReceive(csContext: JContext; csIntent: JIntent); Но ничего не выходит. 
      Я подозреваю, что данные из сканера где-то в структуре csIntent. Но, к сожалению, в отладке дальше адреса этой переменной пробраться не получилось.
      Причем строка ed.Text := JStringToString(csIntent.getAction); возвращает в Text название интента: "android.intent.ACTION_DECODE_DATA".
      Помогите разобраться со структурой JIntent. Спасибо.
       
       
    • От x11
      Предисловие. Приложение (пока в качестве эксперимента для Android)  предназначено для объявлений. Думаю, что все знают про Avito, OLX и т.д. Т.е. для объявлений.
      У каждого объявления, кроме кучи разных параметров (полей) есть фотографии (картинки).
      Есть форма добавления/просмотра/редактирования одного объявления.
      Теперь вопрос. Как лучше и правильней выводить фотографии? С помощью чего? ListBox + TImageControl или ListView + TImageControl? Может быть вместо TImageControl правильней использовать TImageViewer?
      Я пока не понял, в чем кардинальные различия между ImageViewer и ImageControl.
      Может быть в FMX есть что-то вроде слайдера или готовой галереи, чтобы можно было легко загрузить фотки туда и листать.
       
       
      И второй вопрос вдогонку.
      Как/где лучше и правильней хранить сами фотографии? В базе вместе со всеми остальными данными? Или в какой-нибудь папке? Если в папке, то в специальной недоступной пользователю или в какой-нибудь общедоступной и тогда фотки можно ьбудет смотреть и через галерею, и копировать их, и легко отправлять через мессенджеры средствами самого Аднроида + можно к облаку папку(и) подключить.
       
      Если хранить в базе, то в этом случае я вижу более простой способ на случай если использовать приложение и на iOS. Т.е. код загрузки/сохранения фоток один и тот же.
       
      Спасибо.
    • От Barbanel
      Есть такой код:
      if TPlatformServices.Current.SupportsPlatformService(IFMXCameraService, Service) then begin Params.Editable := True; Params.NeedSaveToAlbum := False; Params.RequiredResolution := TSize.Create(self.ClientHeight, self.ClientHeight); Params.OnDidFinishTaking := actTakePhotoFromCameraDidFinishTaking; Service.TakePhoto(btnInfoAddPhoto, Params); end else ShowMessage('This device does not support the camera service'); После того как фото сделано, появляется ошибка EBitmapLoadingFailed 'Loading bitmap failed (/storage/emulated/0/DCIM/Camera/IMG_20170503_170826.jpg).'
      Появилась после того как убрал из проекта права READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE.
       
      Вопрос: можно ли получать фото от камеры минуя их сохранение в память утсройства?
      PS спрашиваю, т.к. стоит задача избавиться от ненужных прав в манифесте.
      Всем заранее спасибо!
    • От Rustam Bikeev
      Здравствуйте. Пришла беда откуда не ждал, Delphi 10.2 не знает библиотеки androidapi.... FMX.Helpers.... и тд и тп. И никак не получается их установить, либо я тупой либо я даже незнаю что может быть. фалы *.dcu лежат в папке ...\lib\android\release.
      Помогите решить проблему очень нужно использовать намерения. уже и переустанавливал платформу android не помогает.

    • От AlexShaman
      procedure TfMain.PhotoClick(Sender: TObject); var sPath,FLastPhotoName:string;   {$IF DEFINED(ANDROID)}   PhotoIntent: JIntent;   photoUri: Jnet_Uri;   JFileName: JFile;   {$ENDIF} begin   {$IF DEFINED(ANDROID)}   FLastPhotoName := '';   FLastPhotoFullName := '';   FMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage(TMessageResultNotification, HandleActivityMessage);   PhotoIntent := TJIntent.JavaClass.init(TJMediaStore.JavaClass.ACTION_IMAGE_CAPTURE);   if (PhotoIntent.resolveActivity(TAndroidHelper.Context.getPackageManager()) <> nil) then   begin     FLastPhotoName := THashMD5.GetHashString('JPEG PHOTO FILE ' + DateTimeToStr(Now)) + '.jpg';     FLastPhotoFullName := TPath.Combine(TPath.GetSharedDocumentsPath, FLastPhotoName);     JFileName := TJFile.JavaClass.init(StringToJString(FLastPhotoFullName));     photoUri := TJnet_Uri.JavaClass.fromFile(JFileName);     PhotoIntent.putExtra(TJMediaStore.JavaClass.EXTRA_OUTPUT,         TJParcelable.Wrap((photoUri as ILocalObject).GetObjectID));     if PhotoIntent.resolveActivity(TAndroidHelper.Activity.getPackageManager) <> nil then       TAndroidHelper.Activity.startActivityForResult(PhotoIntent, REQUEST_IMAGE_CAPTURE);   end;   {$ENDIF}  
      Приложение камеры открывается, фото делается, но при закрытии приложение вылетает
      В логах ошибки
      java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=null} to activity {com.embarcadero.test/com.embarcadero.firemonkey.FMXNativeActivity}: java.lang.NullPointerException
       
    • От ENERGY
      Подскажите пожалуйста как определить что программа запустилась из AlarmManager?
      Я сделал это так, по аналогии с http://stackoverflow.com/questions/6751564/how-to-pass-a-boolean-between-intents
      В Java коде, который компилиться в итоге в classes.dex (с XE7 не нужен DEX файл, можно подключить Jar файл сразу к проекту!), добавил строчку
      public class AlarmReceiver extends BroadcastReceiver {
          public void onReceive(Context context, Intent intent) {
                  Intent TestLauncher = new Intent();                        
                  TestLauncher.setClassName(context, "com.embarcadero.firemonkey.FMXNativeActivity");
                  TestLauncher.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                  TestLauncher.putExtra("StartedFromAM", true);
                  context.startActivity(TestLauncher);
          }
      }
      По идее теперь при старте из AlarmManager, должно быть true в активити.
      Из Delphi пытаюсь получить это значение, но всегда возвращает false (0). 
       ShowMessage(SharedActivity.getIntent.getBooleanExtra(StringToJString('StartedFromAM'), false).ToString );
      И вот так тоже:
      ShowMessage ( MainActivity.getIntent.getBooleanExtra(StringToJString('StartedFromAM'), false).ToString);
      Не подскажете в чем может быть проблема?

       
    • От ivanm38
      Добрый день
      Господа программисты, поделитесь куском кода, если не жалко конечно, мне необходимо делать автоматически фото раз в 5-10 секунд и сохранять его на диск, без вызова стандартной камеры, на андроиде
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

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