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

[Android] Обработка приложением неявного намерения (Intent)


Belov.V.

Вопрос

Приложение зарегистрировано для обработки файла по типу через файл манифеста (неявное намерение).

<intent-filter>  
     <action android:name="android.intent.action.VIEW" />
     <category android:name="android.intent.category.DEFAULT" />
     <data android:mimeType="text/xml" />
</intent-filter>

В списке выбора появляется. Как корректно в приложении получить содержание Intent (имя файла, путь)?

Пробовал реализовать через подписку на получение сообщения на изменение статуса приложения:
TMessageManager.DefaultManager.SubscribeToMessage(TApplicationEventMessage, DoApplicationEventChanged);

При этом событие вызвавшее активацию определить можно, но вот как добраться до Intent не пойму:

procedure TForm1.DoApplicationEventChanged(const Sender: TObject; const Message: TMessage);
var
   Intent   : JIntent;
   app_data : TApplicationEventData;
begin
	if not(Message is TApplicationEventMessage) then exit;
	app_data :=  TApplicationEventMessage(Message).Value;
        case app_data.Event of
          FinishedLaunching, BecameActive : ....
        end;                
end;

Что-то застрял я на этом. Как вообще добраться до открывшего приложение Intent? 
Если реализовывать через IFMXApplicationEventService вопрос в итоге такой же.
 
Если есть описание реализации аналогичного функционала для iOS, буду благодарен.
Изменено пользователем Belov.V.
Ссылка на комментарий

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

  • 0
  • Администраторы

Добрый день,

 

Я думаю эта тема вам поможет: [Android] Как в вызываемом приложении получить результат, объект JIntent?

 

Приложение делает широковещательную рассылку с сообщением TMessageResultNotification

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

Добрый день,

 

Я думаю эта тема вам поможет: [Android] Как в вызываемом приложении получить результат, объект JIntent?

 

Приложение делает широковещательную рассылку с сообщением TMessageResultNotification

 

Ярослав, спасибо!

 

Эту тему смотрел, но попробую еще раз покрутить. У меня почему то TMessageResultNotification.Value всегда неопределенный возвращался (и оба *Code всегда нулевые). В результате из него ничего, кроме вылетания приложения получить не удавалось.

 

Как минимум, если точно делается широковещательная рассылка TMessageResultNotification направление копания понятно.

Ссылка на комментарий
  • 0
Все же путь подписи на получение широковещательную рассылку кмк не верный, т.к. приложение может быть не запущено, а событие в системе уже произошло (к примеру проводится вызов для обработки файла, полученного по почте, а приложение на событие не подписано, т.к. на момент вызова не запущено).
 
В любом случае проверил - приложение активируется (или запускается, или выводится из бека), т.к. зарегистрировано через <intent-filter>. При этом рассылка TMessageResultNotification не поступает.
 
Приведенный выше пример о другом - это вариант обмена данными между своими приложениями. Возможно я что-то не так понимаю. :-(
Ссылка на комментарий
  • 0

Вопрос в продолжение темы.

Добрался до получении файла или пути к нему:

var 
  uri     : Jnet_Uri;
  uriStr  : String;
....
begin
...
    if TJIntent.JavaClass.ACTION_VIEW.equals(intent.getAction) then
    begin
      uri := intent.getData;
      uriStr := JStringToString(intent.getData.getEncodedPath);
 

Не могу вытащить содержимое. Из uri или uriStr разными способами getPath, toString или через JURIToStr() ) можно вытащить строку двух видов (для примера событие активировалось из почтового приложения):

      content: //...../messages/2687/attachments/0.1/BEST/false
      или
      /<почтовый профиль>/messages/2687/attachments/0.1/BEST/false 

Ни пути ни такого файла из приложения не находится. Extras тоже пустой.

 

Как из intent.getData получить контент? Кто мысль подкинет.?

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

Сделал не через подписку, а через событие запуска или вывода приложения из спящего режима.

Регистрация на обработку файла по типу через <intent-filter> в AndroidManifest.xml

 

Как писал выше, метод через подписку не сработает, если приложение на момент создания Intent системой не было запущено. Полную реализацию обязательно выложу, вот только бы до файла добраться.

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

Регистрируем намерение через файл манифеста (ниже для файлов с типом xml). Для Android 3.2 адекватнее работал второй фильтр:

<activity …
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
	<data android:scheme="file" />
	<data android:mimeType="text/xml" />
	<data android:mimeType="application/xhtml+xml" />
</intent-filter>
<!-- Для 3.2 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
	<category android:name="android.intent.category.DEFAULT" />
	<data android:scheme="file" />
	<data android:mimeType="*/*" />
	<data android:host="*" />
	<data android:pathPattern=".*\\.xml" />
	<data android:pathPattern=".*\\..*\\.xml" />
	<data android:pathPattern=".*\\..*\\..*\\.xml" />
	<data android:pathPattern=".*\\..*\\..*\\..*\\.xml" />
	<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.xml" />
</intent-filter>

Регистрируем свой обработчик события для на событие смены состояния приложения:

procedure TForm1.FormCreate(Sender: TObject); 
var
  ApplicationService: IFMXApplicationEventService;
begin

  if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, ApplicationService)
  then ApplicationService.SetApplicationEventHandler(ApplicationEventChanged);

end;

Оформляем обработчик.

Получение файла можно перенести в Form1.onCreate, но я для экспериментов с activity делал тут:

function TForm1.ApplicationEventChanged(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
var
    intent        : JIntent;
    fileFullPath  : String;

begin
  intent := SharedActivity.getIntent;

  // BecameActive
  if AAppEvent = TApplicationEvent.BecameActive then
  begin
    if Assigned(intent) and TJIntent.JavaClass.ACTION_VIEW.equals(intent.getAction) then
    begin
        fileFullPath := JStringToString(intent.getData.getPath);
        if FileExists(fileFullPath) then
        begin
          // обработка файла fileFullPath
          // ...
        end;
    end;
  end
  // завершаем Activity при переводе приложения в "Background"
  else if AAppEvent = TApplicationEvent.EnteredBackground then SharedActivity.finish;
  //

  Result := True;
end;
Хочу обратить внимание, что привел только сам принцип. Очень важно корректно завершить активность (при сворачивании приложения, отключении экрана и т.д. ), т.к. при повторном вызове активности будет вызываться последний из стека.
И если его не закрывать будем получить предыдущий, не закрытый activity. А если за приложением зарегистрировано несколько Activity (приложение можно вызвать из ланчера или как в примере, для обработки файла по типу).... в общем тут есть с чем поиграться.
 
Для желающих посмотреть поведение и последовательность выкладываю пример, который формирует лог обработки.
Нужно закоментировать:
   else if AAppEvent = TApplicationEvent.EnteredBackground then SharedActivity.finish;
И посмотреть что и когда приложение получает в SharedActivity и AAppEvent.
 
 
 
Что не получилось:
1) Некоторые программы отдают файл в схеме «content» (к примеру Gmail v5.1 отдает). Как в этом случае получить содержание файла не разобрался. Поэтому в фильтре ограничил вид контента "file"
2) Не смог добраться до стека Activity. Если приложение в памяти и повторно вызывается с новой activity - возможно вот тут это могло пригодиться. (activity ... android:launchMode="singleTop" в этом случае не помог).
 
ADD: было обсуждение, кто как закрывает приложения на андроид, чтобы не оставалось в памяти. Если добавлять SharedActivity.finish приложение 100% убирается из памяти.
 

 

Intent_Get-File.zip

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

Что не получилось:

1) Некоторые программы отдают файл в схеме «content» (к примеру Gmail v5.1 отдает). Как в этом случае получить содержание файла не разобрался. Поэтому в фильтре ограничил вид контента "file"

Для получения «content» я сделал так

 

var
  Intent: JIntent;
  Uri: Jnet_Uri;
  INStream: JInputStream;
  OUTStream: JOutputStream;
  outputfile: string;
  FFF: JFile;
  NumRead, NumWritten: Longint;
  Buffer: TJavaArray<Byte>;
begin
...
        Intent := SharedActivity.getIntent;
        if (Pos('image/', JStringToString(Intent.getType))>0) or
          (Pos('audio/', JStringToString(Intent.getType))>0) or
          (Pos('video/', JStringToString(Intent.getType))>0) or
          (Pos('application/', JStringToString(Intent.getType))>0)    or
          ('*/*' = JStringToString(Intent.getType)) then
        begin
          if JStringToString(Intent.getType) <> '' then
          begin
            Parcel := Intent.getParcelableExtra(TJIntent.JavaClass.EXTRA_STREAM);
            Uri := TJnet_Uri.Wrap(Parcel);

              if JStringToString(Uri.getScheme) = 'content' then
              begin
                if Pos('/mpeg', JStringToString(Intent.getType)) > 0 then
                  outputfile := 'my.mp3'
                else
                  if Pos('/jpeg', JStringToString(Intent.getType)) > 0 then
                    outputfile := 'my.jpg'
                  else
                    outputfile :=  'my.' + copy(JStringToString(Intent.getType), Pos('/', JStringToString(Intent.getType))+1, 
                       Length(JStringToString(Intent.getType)));//выдергиваем имя типа для того чтобы сделать его расширением
                //на случай если в Intent не уточнен тип, а указана *, например image/*  
	        if Pos('.*', outputfile) > 0 then
                  outputfile := StringReplace(outputfile, '.*', '.tmp', [rfReplaceAll]);

                Buffer := TJavaArray<Byte>.Create(4096);
                INStream := SharedActivityContext.getContentResolver.openInputStream(Uri);
                try
                    FFF := TJFile.JavaClass.init(StringToJString(<ИМЯ_ПАПКИ>), StringToJString(outputfile));
                    FFF.setWritable(true, false);
                    OUTStream := TJFileOutputStream.JavaClass.init(FFF);
                    repeat
                      NumRead := inStream.read(buffer);
                      if (NumRead <= 0) then
                        Break;
                      outStream.write(buffer, 0, NumRead);
                      application.ProcessMessages;
                    until NumRead <= 0;
                    outStream.close;
                    inStream.close;
                except
                  on e: exception do
                    raise Exception.CReate('Error. Can''t copy file');
                end;
              end
          end
        end
end
может не самое оптимальное, но работает, в итоге у вас файл с именем outputfile и в папке <ИМЯ_ПАПКИ>
Ссылка на комментарий
  • 0

Делал тут темы для своего приложения (лаунчера) и встал вопрос об открытии файла темы из проводника. И конечно же уперся в то что intent возвращает всегда один и тот же как при открытии программы.

Итого поздно)), но может кому пригодится 

 <intent-filter>
              <action android:name="android.intent.action.VIEW"/>
         	<action android:name="android.intent.action.EDIT"/>
              <category android:name="android.intent.category.DEFAULT"/>
              <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="file" />
		<data android:scheme="content" />
		<data android:scheme="http" />
		<data android:scheme="https" />
		<data android:host="*" />             
                <data android:pathPattern=".*\\.caskin" />
            </intent-filter>
            <intent-filter>
              <action android:name="android.intent.action.VIEW"/>
	      <action android:name="android.intent.action.EDIT"/>
              <category android:name="android.intent.category.DEFAULT"/>
              <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="file" />
		<data android:scheme="content" />
		<data android:scheme="http" />
		<data android:scheme="https" />
		<data android:host="*" />
                <data android:mimeType="*/*" /> 
		<data android:pathPattern=".*\\.caskin" />
        
            </intent-filter>





private
    var IMessageSubscriptionID: Integer;
    procedure HandleIntentMessage(const Sender: TObject; const M: TMessage);
    function OnNewIntent(Intent: JIntent): Boolean;


procedure TForm1.FormCreate(Sender: TObject);
var
 aFMXApplicationEventService: IFMXApplicationEventService;
begin
    if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(aFMXApplicationEventService)) then
       aFMXApplicationEventService.SetApplicationEventHandler(HandleAppEvent);

	MainActivity.registerIntentAction(TJIntent.JavaClass.ACTION_VIEW);
    IMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage(
    TMessageReceivedNotification, HandleIntentMessage);
end;

function TForm1.OnNewIntent(Intent: JIntent): Boolean;
var
  fPath: String;
begin
  Result := False;
  if Intent <> nil then
  begin
   fPath:=  Intent.getData.getEncodedPath;
  end;
end;


procedure TForm1.HandleActivityMessage(const Sender: TObject; const M: TMessage);
begin
  if M is TMessageResultNotification then
    OnActivityResult(TMessageResultNotification(M).RequestCode, TMessageResultNotification(M).ResultCode,
      TMessageResultNotification(M).Value);
end;


function TForm1.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
var
myIntent: JIntent;
insSkinPath: String;
begin
  case AAppEvent of
    TApplicationEvent.FinishedLaunching:
    begin
    try
     myIntent := MainActivity.getIntent;
    if myIntent <> nil then
      OnNewIntent(myIntent);
       except
       end;
    end;

  end;

  Result := True;

end;

 

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

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

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

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

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

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

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

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

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

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