2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-12-27

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

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

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

※画像ファイルを送る場合はTShowShareSheetActionを使いましょう!

##とりあえず実例
design.png
画像のようにコントロールを配置します。
保存ボタンを押すと、Edit1.Textをファイル名としたファイルを作成し、ローカルに保存します。
送るボタンを押すと、インテント発生させ、ファイルを外部に出力します。

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

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;

保存先にTPath.GetTempPathを指定します(GetPublicPath+'/Tmp'に等しいパスです。参考:(AndroidアプリケーションでSystem.IOUtils.TPathのメソッドが返すディレクトリについての簡単なまとめ )。

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

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

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

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


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;

setTypeに渡すMIMEタイプについては、送信するメディアに応じて設定してください。(メールの場合message/rfc822にする、など。  参考:MIME の基礎

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

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

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

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

(このあたりの事情については以下を参考にしました。Android 画像ファイルを扱う際のFileとUriまとめ

なお、JFileToJURI(JFile)の内部では、(動作環境がAndroidOS7以上の場合)FileProviderを使用し、FileをUriに変換しています。
FileProviderを動作させるためには、IDEのプロジェクト→オプション→資格リストにて、「セキュアファイル共有」にチェックを入れる(Trueにする)必要があります。
error.png(実行時にこういうエラーが出ます)

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

以上です!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?