概要
ウェブやメールで特定のファイルをダウンロードして、そのファイルを自分が作ったアプリで開きたい時があります。例えば、下記のようにpdfやエクセルファイルを特定のアプリを開けます。
アンドロイドの場合、
iOSの場合、
上のように特定のファイルを開く時、自分のアプリが表示されるようにする方法とそのファイルを使う方法について説明します。このブログでは.temp
ファイルを使ってアプリを実行する方法を説明します。
iOS
iOSでファイルが上手くコピーされたか確認するため、下にあるブログを見てアプリのiTunesファイル共有機能を入れます。
今iOSで特定のファイルを自分のアプリで開けるようにするため、ios/[project name]/Info.plist
を開いて下記のように修正します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
...
<!-- custom file start -->
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Custom File</string>
<key>UTTypeIdentifier</key>
<!-- change custom file extension -->
<string>com.example.document.temp</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<!-- change custom file extension -->
<array>
<string>temp</string>
</array>
</dict>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Custom File</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<!-- change custom file extension -->
<string>com.example.document.temp</string>
</array>
</dict>
</array>
<!-- custom file end -->
...
</dict>
</plist>
もう一度話しますが、上の内容は.temp
で終わるファイルを自分のアプリで開く場合、使う内容です。<!-- change custom file extension -->
でコメントアウトがある3つの部分を自分が欲しいファイルで修正して使ってください。
まず、iTunesを開いてファイル共有機能が活性化されたか確認します。
テストのためファイルを.temp
に修正してiOSで確認できるメールで添付して送信します。メールアプリを開いて先ほど送った.temp
ファイルを選択して右上の共有ボタアンを押すと下記のように自分のアプリでコピーできることが確認できます。
上の状態で自分のアプリを選択するとアプリが起動してファイルがコピーされます。
また、iTunesを開いて自分のアプリを選択すると下記のようにInbox
フォルダが生成されたことが確認できます。
このファイルを外にコピーして中の内容を確認するとメールで送ったファイルと同じファイルがあることが確認できます。
アンドロイド
アンドロイドはiOSより少し難しいです。まず、android/app/src/main/AndroidManifest.xml
を開いて下記のようにIntent Filter
を追加します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.react_native_open_file_with">
...
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- custom file start -->
<intent-filter
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<!-- change custom file extension -->
<data
android:mimeType="*/*"
android:host="*"
android:pathPattern=".*\\.temp"
/>
</intent-filter>
<!-- custom file end -->
...
</manifest>
ここにも.temp
を欲しいファイル名で修正して使ってください。MainActivity.java
ファイルを開いて下記の内容を追加します。
...
import android.content.ContentResolver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
...
public class MainActivity extends ReactActivity {
...
@Override
protected void onResume() {
super.onResume();
Uri data = getIntent().getData();
if(data != null) {
try {
importData(data);
}catch (Exception e) {
Log.e("File Import Error", e.getMessage());
}
}
}
private void importData(Uri data) {
final String scheme = data.getScheme();
if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
try {
ContentResolver cr = getApplicationContext().getContentResolver();
InputStream is = cr.openInputStream(data);
if(is == null) return;
String name = getContentName(cr, data);
PackageManager m = getPackageManager();
String s = getPackageName();
PackageInfo p = m.getPackageInfo(s, 0);
s = p.applicationInfo.dataDir;
InputStreamToFile(is, s + "/files/" + name);
} catch (Exception e) {
Log.e("File Import Error", e.getMessage());
}
}
}
private String getContentName(ContentResolver resolver, Uri uri){
Cursor cursor = resolver.query(uri, null, null, null, null);
cursor.moveToFirst();
int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
if (nameIndex >= 0) {
return cursor.getString(nameIndex);
} else {
return null;
}
}
private void InputStreamToFile(InputStream in, String file) {
try {
OutputStream out = new FileOutputStream(new File(file));
int size = 0;
byte[] buffer = new byte[1024];
while ((size = in.read(buffer)) != -1) {
out.write(buffer, 0, size);
}
out.close();
}
catch (Exception e) {
Log.e("MainActivity", "InputStreamToFile exception: " + e.getMessage());
}
}
...
}
上の内容はファイルを選択してアプリを実行する時、そのファイルを自分のアプリフォルダにコピーするコードです。iOSでは自動にアプリフォルダにコピーされますが、アンドロイドは上のようにアプリフォルダにコピーするようにコーディングする必要があります。
iOSでテストしたようにファイルの名前を.temp
で修正してアンドロイドで確認できるメールに添付して送信します。メールアプリを起動して先送った.temp
ファイルを選択するか、ダウンロードしたファイルを選択したら下記のように自分のアプリにコピーすることができます。
上の状態で自分のアプリを選択したらアプリが起動されてファイルがコピーされます。ファイルは/data/user/0/[app package name]/files
に保存されます。
活用
このようにコピーしたファイルをreact-native-fs
を使って活用する方法について説明します。ここで紹介するソースコードはリアクトネイティブ(React Native)に下記の内容を設定したプロジェクトです。それそれが気になる方は下記のリンクを確認してください。
全てのソースは下記のレポジトリ(Repository)で確認することができます。
- githubレポジトリ(Repository): react_native_open_file_with
リアクトネイティブ(React Native)でreact-native-fs
をインストールする方法は下記のブログを参考してください。
全体ソースで重要な部分だけ説明します。
private _DOCUMENT_PATH = RNFS.DocumentDirectoryPath;
コピーされたファイルはRNFS.DocumentDirectoryPath
に保存されます。
private _loadFiles = async () => {
if (Platform.OS === 'ios') {
await this._moveInboxFiles();
}
RNFS.readDir(this._DOCUMENT_PATH)
.then((srcFiles: Array<RNFS.ReadDirItem>) => {
let files: Array<RNFS.ReadDirItem> = [];
srcFiles.map((file: RNFS.ReadDirItem) => {
if (file.isFile() && file.name.indexOf('.temp') >= 0) {
files.push(file);
}
});
this.setState({ files });
})
.catch(err => {
console.log(err.message, err.code);
});
};
そのフォルダを読んでファイルの場合ファイルリストに保存します。iOS
の場合、RNFS.DocumentDirectoryPath
中でInbox
フォルダに保存されます。したがって、ファイルを読む前、下記のようにそのファイルをコピーします。
private _moveInboxFiles = async () => {
try {
const inboxFiles = await RNFS.readDir(this._DOCUMENT_PATH + '/Inbox');
if (inboxFiles) {
inboxFiles.map(async file => {
if (file.isFile()) {
if (file.isFile()) {
await RNFS.moveFile(
file.path,
`${this._DOCUMENT_PATH}/${file.name}`
);
}
}
});
}
} catch (err) {
console.log(err.message, err.code);
}
};
このファイルは下記のような情報を持っています。必要な情報を自由に使ってください。
const { ctime, mtime, path, name, size } = file;
iOSシミュレータテスト
今まで作った内容をiOSシミュレータで確認する方法を説明します。シミュレータが起動中の状態で下記のコマンドでシミュレータアイディを取得します。
xcrun simctl list | egrep '(Booted)'
私が作ったレポジトリ(Repository)ではnpmコマンドを作っております。pacakage.json
を確認してください。
- githubレポジトリ(Repository): react_native_open_file_with
npm run get-id
iPhone X (5C0277D7-12FA-42DE-AD6D-AC3C74324B4C) (Booted)
コマンドを実行したら上のようにシミュレータのアイディを取得することができます。
cd /Users/[user name]/Library/Developer/CoreSimulator/Devices/5C0277D7-12FA-42DE-AD6D-AC3C74324B4C/data/Containers/Data/Application/
open .
そのフォルダに移動してフォルダの中を見たら今日の日付のアプリを探すことができます。アプリの下にあるDocuments
フォルダにファイルをコピーします。
それでアプリを再実行や私のレポジトリ(Repository)でテストしてる方は右上のリフレッシュボタンを押します。
そしたら上のようにファイルが上手く表示されることが確認できます。
アンドロイドテスト
私はアンドロイドはデバイスでテストしました。アンドロイドでも下記のようにファイルが上手く表示することが確認できます。
完了
最近は全てのファイルをサーバーに保存して活用してるのでこのような機能が要らないかもしれないです。それでも覚えておくといつかは使えると思います。これで特定のファイルでアプリを実行して活用する方法について見て見ました。特定のファイルでアプリを実行したい方にちょっとでも参考になったら嬉しいです。
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Deku
が開発したアプリを使ってみてください。Deku
が開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。