Delphiでインテントを使ってファイルを渡してみる

#はじめに
タイトルの通りなのですが、手探りだった為にあやふやな点が多く、記事の公開後に訂正が入る可能性があります。ご了承ください。

発端としては、自作アプリケーションで生成したデータをGoogleドライブに出力するにはどうしたらいいんだろう？というものです。FMXもAndroid開発もわからん・・という人が書いています。

Delphi 10.3 CommunityEditionにて動作確認しています。

※画像ファイルを送る場合は[TShowShareSheetAction](http://docwiki.embarcadero.com/Libraries/Rio/ja/FMX.MediaLibrary.Actions.TShowShareSheetAction)を使いましょう！

##とりあえず実例
![design.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33684/00412de9-617c-6837-f3a5-cdbc20b7d001.png)
画像のようにコントロールを配置します。
保存ボタンを押すと、<code>Edit1.Text</code>をファイル名としたファイルを作成し、ローカルに保存します。
送るボタンを押すと、インテント発生させ、ファイルを外部に出力します。



###「保存」ボタンのイベント

```pascal
procedure TForm1.Button1Click(Sender: TObject);
var
  filepath:String;
begin
  if Edit1.Text.IsEmpty then Exit;

  filepath := TPath.Combine(TPath.GetTempPath,Edit1.Text);

  if TFile.Exists(filepath) then
    TFile.Delete(filepath);

  memo1.Lines.SaveToFile(filepath);
end;
```
保存先に<code>TPath.GetTempPath</code>を指定します（GetPublicPath+'/Tmp'に等しいパスです。参考：（[AndroidアプリケーションでSystem.IOUtils.TPathのメソッドが返すディレクトリについての簡単なまとめ](https://www.gesource.jp/weblog/?p=6625)　）。


###「送る」ボタンのイベント

先に以下のソースコードをリンクしておく必要があります。

```pascal
uses 
  Androidapi.JNIBridge,      //ILocalObject
  Androidapi.JNI.Os,         //JParcelable
  Androidapi.JNI.Net,        //Jnet_Url
  Androidapi.JNI.JavaTypes,  //JFile
  Androidapi.JNI.GraphicsContentViewText, // JIntent
  Androidapi.Helpers,        //StringToJString, TAndroidHelper.JFileToJURI
  FMX.Platform.Android;      //MainActivity
```

ボタンイベントは以下になります。

```pascal

procedure TForm1.Button2Click(Sender: TObject);
var
  filepath:String;
  SendFile: JFile;
  Uri:JParcelable;
  intent: JIntent;
begin
  if Edit1.Text.IsEmpty then Exit;

  filepath := TPath.Combine(TPath.GetTempPath,Edit1.Text);
  if not TFile.Exists(filepath) then Exit;

  try
    SendFile := TJFile.JavaClass.init( StringToJString( filepath ));
    Uri := JParcelable(TAndroidHelper.JFileToJURI(SendFile));

    intent := TJIntent.Create;
    intent.setAction(TJIntent.JavaClass.ACTION_SEND);
    intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, Uri);
    intent.setType(StringToJString('application/octet-stream'));

    MainActivity.startActivity(intent);

  except on e:Exception do
    ShowMessage(e.Message);
  end;
end;
```

<code>setType</code>に渡すMIMEタイプについては、送信するメディアに応じて設定してください。（メールの場合<code>message/rfc822</code>にする、など。　 参考：[MIME の基礎](https://www.mew.org/~kazu/doc/newsletter/3.html) ）

##解説めいたもの
インテントはAndroid独自の仕組みなので、Firemonkeyフレームワークではなく、Androidの機能を薄くラップしたライブラリを使用します。そのため、インテント作成～送信部分は、Javaで書いたAndroidアプリのソースコードそっくりです。

Javaのサンプルを眺めると、インテントでファイルを渡す場合<code>intent.putExtra(Intent.EXTRA_STREAM, <対象のUri>)</code>という方法で行うので、Delphiの場合もそれに倣ってJNet_Uri型変数を作成し、JParcelable型でラップして渡しています（なぜParcelable型にラップする必要があるのかはわかりませんでした）。

その<code>JNet_Uri</code>を取得するために、まずファイルパスから<code>JFile</code>型オブジェクトを作成します。

```pascal
    SendFile := TJFile.JavaClass.init( StringToJString( filepath ));
    Uri := JParcelable( TAndroidHelper.JFileToJURI(SendFile) );
```
（このあたりの事情については以下を参考にしました。[Android 画像ファイルを扱う際のFileとUriまとめ](https://qiita.com/wakamesoba98/items/98b79bdfde19612d12b0)）


なお、<code>JFileToJURI(JFile)</code>の内部では、（動作環境がAndroidOS7以上の場合）FileProviderを使用し、FileをUriに変換しています。
FileProviderを動作させるためには、IDEのプロジェクト→オプション→資格リストにて、「セキュアファイル共有」にチェックを入れる（Trueにする）必要があります。
![error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33684/c9bbb69f-9166-8f4f-56b8-217a4ff086ce.png)（実行時にこういうエラーが出ます）



チェックを入れた後にコンパイルすると、出力フォルダ（Android\Debug\）内の<code>AndroidManifest.xml</code>に、FileProviderに関するタグが自動で追加されてるのが確認できます。また、FileProviderの設定となる<code>provider_paths.xml</code>も追加されています。
配置マネージャでも<code>provider_paths.xml</code>も追加されてることが確認できます。カスタマイズした<code>provider_paths.xml</code>を使う場合は、配置マネージャで自動追加された方のファイルと切り替えておけばいいのかなと思います。


以上です！