Delphi 10 SeattleのAndroidアプリケーションでIntentの送受信をするには

Delphi 10 SeattleのAndroidアプリケーションでIntentを受信する方法です。

以前に書いた「Delphi XE8のFiremoneky AndroidアプリケーションでIntentを受信するには」には、Activity.OnNewIntentを処理していないという問題がありましたので、修正しました。

文字列を他のアプリケーションに送信する

新しいマルチデバイスアプリケーションを作成し、フォームにTButtonとTMemoを配置します。

intent01

ボタンを押すと、入力した文字列を他のアプリケーションに送信します。

Button1を押したときのイベントを記述します。

JIntentを作成して、送信するデータの設定を行います。

  Intent := TJIntent.Create;
  Intent.setType(StringToJString('text/plain'));
  Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringToJString(AText));

Android APIのPackageManagerクラスのqueryIntentActivitiesメソッドを使用して、インテントを処理できるアプリケーションの有無を確認しています。
処理できるアプリケーションがあれば、インテントを送信します。

  if MainActivity.getPackageManager.queryIntentActivities(Intent,
    TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY).size > 0 then
    MainActivity.startActivity(Intent)
  else
    ShowMessage('Receiver not found');

ソースコードは次のようになります。

uses
  Androidapi.JNI.GraphicsContentViewText, // JIntent
  Androidapi.Helpers, // StringToJString
  FMX.Platform.Android; // MainActivity

procedure TForm1.Button1Click(Sender: TObject);
var
  AText: string;
  Intent: JIntent;
begin
  AText := Memo1.Text;

  Intent := TJIntent.Create;
  Intent.setType(StringToJString('text/plain'));
  Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringToJString(AText));

  if MainActivity.getPackageManager.queryIntentActivities(Intent,
    TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY).size > 0 then
    MainActivity.startActivity(Intent)
  else
    ShowMessage('Receiver not found');
end;

intent02

intent03

送信された文字列を受信する

新しいマルチデバイスアプリケーションを作成し、フォームにTMemoを配置します。

intent04

AndroidManifest.template.xmlを編集する

アプリケーションをビルドすると「AndroidManifest.template.xml」が生成されます。
「AndroidManifest.template.xml」をテキストエディタで開き、次の行を追加します。

        <intent-filter>  
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter> 
        //↓追加
        <intent-filter>  
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter> 

これでインテントを受信できるようになります。

文字列を受信する

インテントから文字列を受け取ります。

フォームのOnCreateイベントを記述します。

アプリケーションのイベントハンドラ(HandleAppEvent)を設定し、アプリケーションがアクティブになったときのイベントを取得するようにします。

procedure TForm2.FormCreate(Sender: TObject);
var
  AppEventService: IFMXApplicationEventService;
begin
  if TPlatformServices.Current.SupportsPlatformService
    (IFMXApplicationEventService, AppEventService) then
    AppEventService.SetApplicationEventHandler(HandleAppEvent);
end;

フォームにHandleAppEventメソッドを追加します。

type
  TForm2 = class(TForm)
  private
    function HandleAppEvent(AAppEvent: TApplicationEvent;
      AContext: TObject): Boolean;

アプリケーションがアクティブになったとき、インテントがあればHandleIntentActionメソッドを呼び出します。

function TForm2.HandleAppEvent(AAppEvent: TApplicationEvent;
  AContext: TObject): Boolean;
var
  StartupIntent: JIntent;
begin
  Result := False;
  if AAppEvent = TApplicationEvent.BecameActive then
  begin
    StartupIntent := MainActivity.getIntent;
    if StartupIntent <> nil then
      HandleIntentAction(StartupIntent);
  end;
end;

フォームにHandleIntentActionメソッドを追加します。

 type
  TForm2 = class(TForm)
  private
    function HandleIntentAction(const Data: JIntent): Boolean;

インテントから送信された文字列を取り出します。

function TForm2.HandleIntentAction(const Data: JIntent): Boolean;
var
  Extras: JBundle;
begin
  Result := False;

  if Data <> nil then
  begin
    Memo1.ClearContent;
    Extras := Data.getExtras;
    if Extras <> nil then
      Memo1.Text := JStringToString
        (Extras.getString(TJIntent.JavaClass.EXTRA_TEXT));
    Invalidate;
  end;
end;

アプリケーションが終了状態の時は問題なく動作しますが、アプリケーションが待機中の時はインテントを受信できません。

待機中のアプリケーションがインテントを受信すると、ActivityのOnNewIntentが呼び出されます。  
OnNewIntentが呼ばれたときにインテントを処理できるようにします。

FormのOnCreateイベントに次のコードを追加します。
MainActivityのregisterIntentActionメソッドで、TJIntent.JavaClass.ACTION_SENDを受け取るようにします。
これによって、ActivityのOnNewIntentが呼ばれたときに、TJIntent.JavaClass.ACTION_SENDのインテントがあると通知が来るようになります。
通知はHandleActivityMessageメソッドで受け取ります。

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

  //次のコードを追加
  MainActivity.registerIntentAction(TJIntent.JavaClass.ACTION_SEND);
  TMessageManager.DefaultManager.SubscribeToMessage
    (TMessageReceivedNotification, HandleActivityMessage);
end;

フォームにHandleActivityMessageメソッドを追加します。

type
  TForm2 = class(TForm)
  private
    procedure HandleActivityMessage(const Sender: TObject; const M: TMessage);

ここでHandleIntentActionメソッドを呼び出すと、アプリケーションがアクティブになったときにHandleAppEventメソッドで上書きされてしまいます。
そこで、インテントを受け取ったら、MainActivityのインテントを更新するようにします。
※ここの処理は検証が不十分です。問題があるかもしれません。

procedure TForm2.HandleActivityMessage(const Sender: TObject;
  const M: TMessage);
var
  Intent: JIntent;
begin
  if M is TMessageReceivedNotification then
  begin
    Intent := TMessageReceivedNotification(M).Value;
    if Intent <> nil then
      MainActivity.setIntent(Intent);
  end;
end;

以上で、インテントを受信できるようになりました。

intent05

コメント

  1. ちょっとそれは、Delphi XE7で可能です。そして、私を助けてください。
    ありがとう。

  2. MainActivityが未定義の識別子になります。
    どこかで指定するのでしょうか?

  3. 山本さん、こんにちは。長町です。
    データの送受信はできるようになったのですが、
    他のアプリから受信できるようにするにはどのようにすれば良いのでしょうか。

    などしてみましたが、Androidが受け付けていないようです。
    この場合、INTENT受信ではない?

  4. 古い記事に失礼します。
    アプリの未起動時のインテント処理としてBecameActiveで制御されていますが
    この場合、アプリのフォアグラウンド移行の度に、起動時のインテントを引き継いだまま
    何度も処理が走ってしまいます。
    インテント受信時のみ処理される、といった形が望ましいかと思いますので
    FinishedLaunchingを使用した方が良いかと。
    (未起動時に1回だけ実行。起動中はregisterIntentActionからのHandleActivityMessageで処理)

    Berlinでは上記で問題ないこと確認しました。
    SeattleでもTApplicationEventの内容は変わっていないので、問題ないかと存じます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください