回答編集履歴
6
修正
test
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
---
|
10
10
|
以下の環境を構築してプラグインを作って見ました。
|
11
|
-
main.dart を実行すると(?) StrageAccessFramework のアクティビティで Downloads フォルダが表示されます。SAVE ボタンを押すと
|
11
|
+
main.dart を実行すると(?) StrageAccessFramework のアクティビティで Downloads フォルダが表示されます。フォルダやファイル名を変更しSAVE ボタンを押すとファイルが作られ、 Flutter の画面が出て(戻って) 該当ファイルのコンテンツプロバイダでの URI が表示されます。
|
12
12
|
…で、これはどうすれば(pub なんとかに公開する以外で)プラグインとして GitHub とかで公開できるんでしょうか(X_X;;;
|
13
13
|
|
14
14
|
Flutter
|
5
追加
test
CHANGED
@@ -5,3 +5,225 @@
|
|
5
5
|
(Android10 以前では ) メディアファイル以外のファイルは [ストレージ アクセス フレームワーク](https://developer.android.com/training/data-storage/shared/documents-files?hl=ja) を用いるのが良いかもしれません。
|
6
6
|
Flutter プラグインを探しましたら以下がありました。※ホンモノかどうか私には分かりませんので、一応ご注意ください。
|
7
7
|
[saf: ^1.0.3+3](https://pub.dev/packages/saf)
|
8
|
+
|
9
|
+
---
|
10
|
+
以下の環境を構築してプラグインを作って見ました。
|
11
|
+
main.dart を実行すると(?) StrageAccessFramework のアクティビティで Downloads フォルダが表示されます。SAVE ボタンを押すと test.csv ファイルが作られ、 Flutter の画面が出て(戻って) 該当ファイルのコンテンツプロバイダでの URI が表示されます。
|
12
|
+
…で、これはどうすれば(pub なんとかに公開する以外で)プラグインとして GitHub とかで公開できるんでしょうか(X_X;;;
|
13
|
+
|
14
|
+
Flutter
|
15
|
+
```plain
|
16
|
+
>flutter --version
|
17
|
+
Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git
|
18
|
+
Framework • revision c860cba910 (2 weeks ago) • 2022-03-25 00:23:12 -0500
|
19
|
+
Engine • revision 57d3bac3dd
|
20
|
+
Tools • Dart 2.16.2 • DevTools 2.9.2
|
21
|
+
```
|
22
|
+
AndroidStudio
|
23
|
+
```plain
|
24
|
+
Android Studio Bumblebee | 2021.1.1 Patch 2
|
25
|
+
Build #AI-211.7628.21.2111.8193401, built on February 17, 2022
|
26
|
+
Runtime version: 11.0.11+9-b60-7590822 amd64
|
27
|
+
VM: OpenJDK 64-Bit Server VM by Oracle Corporation
|
28
|
+
Windows 10 10.0
|
29
|
+
GC: G1 Young Generation, G1 Old Generation
|
30
|
+
Memory: 1280M
|
31
|
+
Cores: 6
|
32
|
+
Registry: external.system.auto.import.disabled=true
|
33
|
+
Non-Bundled Plugins: Dart (211.7811), org.jetbrains.kotlin (211-1.6.20-release-275-AS7442.40), io.flutter (66.0.1)
|
34
|
+
```
|
35
|
+
AndroidStudio Plugin
|
36
|
+
- Flutter flutter.dev 66.0.1
|
37
|
+
- Dart JetBrains 211.7811
|
38
|
+
|
39
|
+
plugin サンプル生成コマンド(うろ覚え...)
|
40
|
+
```plain
|
41
|
+
> flutter create --org com.teratail.q_jmx3uadrrp6bi3 -t plugin --platforms android -a java saf_plugin_sample
|
42
|
+
```
|
43
|
+
|
44
|
+
**サンプルから修正したコード**
|
45
|
+
|
46
|
+
saf_plugin_sample/example/lib/main.dart
|
47
|
+
```dart
|
48
|
+
import 'package:flutter/material.dart';
|
49
|
+
import 'dart:async';
|
50
|
+
|
51
|
+
import 'package:flutter/services.dart';
|
52
|
+
import 'package:saf_plugin_sample/saf_plugin_sample.dart';
|
53
|
+
|
54
|
+
void main() {
|
55
|
+
runApp(const MyApp());
|
56
|
+
}
|
57
|
+
|
58
|
+
class MyApp extends StatefulWidget {
|
59
|
+
const MyApp({Key? key}) : super(key: key);
|
60
|
+
|
61
|
+
@override
|
62
|
+
State<MyApp> createState() => _MyAppState();
|
63
|
+
}
|
64
|
+
|
65
|
+
class _MyAppState extends State<MyApp> {
|
66
|
+
String _uri = 'Unknown';
|
67
|
+
|
68
|
+
@override
|
69
|
+
void initState() {
|
70
|
+
super.initState();
|
71
|
+
initPlatformState();
|
72
|
+
}
|
73
|
+
|
74
|
+
// Platform messages are asynchronous, so we initialize in an async method.
|
75
|
+
Future<void> initPlatformState() async {
|
76
|
+
String uri;
|
77
|
+
try {
|
78
|
+
uri = await SafPluginSample.createDocument("text/csv", "test.csv", "AAA,BBB,CCC\n111,222,333") ?? 'Canceled';
|
79
|
+
} on PlatformException {
|
80
|
+
uri = 'Exception';
|
81
|
+
}
|
82
|
+
|
83
|
+
if (!mounted) return;
|
84
|
+
|
85
|
+
setState(() {
|
86
|
+
_uri = uri;
|
87
|
+
});
|
88
|
+
}
|
89
|
+
|
90
|
+
@override
|
91
|
+
Widget build(BuildContext context) {
|
92
|
+
return MaterialApp(
|
93
|
+
home: Scaffold(
|
94
|
+
appBar: AppBar(
|
95
|
+
title: const Text('Plugin example app'),
|
96
|
+
),
|
97
|
+
body: Center(
|
98
|
+
child: Text('Running on: $_uri\n'),
|
99
|
+
),
|
100
|
+
),
|
101
|
+
);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
```
|
105
|
+
saf_plugin_sample/lib/saf_plugin_sample.dart
|
106
|
+
```dart
|
107
|
+
import 'dart:async';
|
108
|
+
|
109
|
+
import 'package:flutter/services.dart';
|
110
|
+
|
111
|
+
class SafPluginSample {
|
112
|
+
static const MethodChannel _channel = MethodChannel('com.teratail.q_jmx3uadrrp6bi3/saf_plugin_sample');
|
113
|
+
|
114
|
+
static Future<String?> createDocument(String type, String title, String contents) async {
|
115
|
+
final String? uri = await _channel.invokeMethod(
|
116
|
+
'createDocument',
|
117
|
+
<String, dynamic>{
|
118
|
+
'type': type,
|
119
|
+
'title': title,
|
120
|
+
'contents': contents
|
121
|
+
});
|
122
|
+
return uri;
|
123
|
+
}
|
124
|
+
}
|
125
|
+
```
|
126
|
+
saf_plugin_sample/android/src/main/java/com/teratail/q_jmx3uadrrp6bi3/saf_plugin_sample/SafPluginSamplePlugin.java
|
127
|
+
```java
|
128
|
+
package com.teratail.q_jmx3uadrrp6bi3.saf_plugin_sample;
|
129
|
+
|
130
|
+
import android.app.Activity;
|
131
|
+
import android.content.Intent;
|
132
|
+
import android.net.Uri;
|
133
|
+
|
134
|
+
import androidx.annotation.NonNull;
|
135
|
+
|
136
|
+
import java.io.*;
|
137
|
+
|
138
|
+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
139
|
+
import io.flutter.embedding.engine.plugins.activity.*;
|
140
|
+
import io.flutter.plugin.common.*;
|
141
|
+
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
142
|
+
import io.flutter.plugin.common.MethodChannel.Result;
|
143
|
+
|
144
|
+
/** SafPluginSamplePlugin */
|
145
|
+
public class SafPluginSamplePlugin implements FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener {
|
146
|
+
private static final int REQUEST_CODE = 1;
|
147
|
+
|
148
|
+
private MethodChannel channel;
|
149
|
+
private ActivityPluginBinding binding;
|
150
|
+
private Result result;
|
151
|
+
private String contents;
|
152
|
+
|
153
|
+
@Override
|
154
|
+
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
155
|
+
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "com.teratail.q_jmx3uadrrp6bi3/saf_plugin_sample");
|
156
|
+
channel.setMethodCallHandler(this);
|
157
|
+
}
|
158
|
+
|
159
|
+
@Override
|
160
|
+
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
161
|
+
channel.setMethodCallHandler(null);
|
162
|
+
}
|
163
|
+
|
164
|
+
@Override
|
165
|
+
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
166
|
+
if(call.method.equals("createDocument")) {
|
167
|
+
if(binding != null) {
|
168
|
+
this.result = result;
|
169
|
+
|
170
|
+
String type = call.argument("type");
|
171
|
+
String title = call.argument("title");
|
172
|
+
contents = call.argument("contents");
|
173
|
+
|
174
|
+
Intent intent = new Intent();
|
175
|
+
intent.setAction(Intent.ACTION_CREATE_DOCUMENT);
|
176
|
+
intent.setType(type);
|
177
|
+
intent.putExtra(Intent.EXTRA_TITLE, title);
|
178
|
+
//intent.putExtra(EXTRA_CONTENTS, contents);
|
179
|
+
binding.getActivity().startActivityForResult(intent, REQUEST_CODE);
|
180
|
+
}
|
181
|
+
} else {
|
182
|
+
result.notImplemented();
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
@Override
|
187
|
+
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
188
|
+
switch(requestCode) {
|
189
|
+
case REQUEST_CODE:
|
190
|
+
if(resultCode != Activity.RESULT_OK) {
|
191
|
+
result.success(null); //CANCEL?
|
192
|
+
return true;
|
193
|
+
}
|
194
|
+
Uri uri = data.getData();
|
195
|
+
try(OutputStream os = binding.getActivity().getContentResolver().openOutputStream(uri);
|
196
|
+
PrintStream ps = new PrintStream(os);) {
|
197
|
+
ps.print(contents);
|
198
|
+
result.success(uri.toString());
|
199
|
+
} catch(IOException e) {
|
200
|
+
result.error("IOException", e.getLocalizedMessage(), e);
|
201
|
+
}
|
202
|
+
return true;
|
203
|
+
}
|
204
|
+
return false;
|
205
|
+
}
|
206
|
+
|
207
|
+
@Override
|
208
|
+
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
|
209
|
+
this.binding = binding;
|
210
|
+
binding.addActivityResultListener(this);
|
211
|
+
}
|
212
|
+
|
213
|
+
@Override
|
214
|
+
public void onDetachedFromActivity() {
|
215
|
+
binding.removeActivityResultListener(this);
|
216
|
+
binding = null;
|
217
|
+
}
|
218
|
+
|
219
|
+
@Override
|
220
|
+
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
|
221
|
+
onAttachedToActivity(binding);
|
222
|
+
}
|
223
|
+
|
224
|
+
@Override
|
225
|
+
public void onDetachedFromActivityForConfigChanges() {
|
226
|
+
onDetachedFromActivity();
|
227
|
+
}
|
228
|
+
}
|
229
|
+
```
|
4
修正
test
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Flutter は全く分からないのですが、 Android で「アプリの固有フォルダ」と言えば、そのアプリ以外からはアクセス出来ないと思います。
|
2
2
|
|
3
|
-
仰られるように、 Android はバージョン毎に「公開したいファイルをどこに作ればいいのか」が[コロコロ変わっている印象](https://developer.android.com/training/data-storage/use-cases?hl=ja
|
3
|
+
仰られるように、 Android はバージョン毎に「公開したいファイルをどこに作ればいいのか」が[コロコロ変わっている印象](https://developer.android.com/training/data-storage/use-cases?hl=ja)ですので、お使いの Android のバージョンと Flutter でのフォルダ/ディレクトリの得方をしっかりマッチングさせて調べる必要があるように思います。
|
4
4
|
|
5
5
|
(Android10 以前では ) メディアファイル以外のファイルは [ストレージ アクセス フレームワーク](https://developer.android.com/training/data-storage/shared/documents-files?hl=ja) を用いるのが良いかもしれません。
|
6
6
|
Flutter プラグインを探しましたら以下がありました。※ホンモノかどうか私には分かりませんので、一応ご注意ください。
|
3
修正
test
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Flutter は全く分からないのですが、 Android で「アプリの固有フォルダ」と言えば、そのアプリ以外からはアクセス出来ないと思います。
|
2
2
|
|
3
|
-
仰られるように、 Android はバージョン毎に「公開したいファイルをどこに作ればいいのか」がコロコロ変わっている印象ですので、お使いの Android のバージョンと Flutter でのフォルダ/ディレクトリの得方をしっかりマッチングさせて調べる必要があるように思います。
|
3
|
+
仰られるように、 Android はバージョン毎に「公開したいファイルをどこに作ればいいのか」が[コロコロ変わっている印象](https://developer.android.com/training/data-storage/use-cases?hl=ja#export-files-to-device)ですので、お使いの Android のバージョンと Flutter でのフォルダ/ディレクトリの得方をしっかりマッチングさせて調べる必要があるように思います。
|
4
4
|
|
5
5
|
(Android10 以前では ) メディアファイル以外のファイルは [ストレージ アクセス フレームワーク](https://developer.android.com/training/data-storage/shared/documents-files?hl=ja) を用いるのが良いかもしれません。
|
6
6
|
Flutter プラグインを探しましたら以下がありました。※ホンモノかどうか私には分かりませんので、一応ご注意ください。
|
2
追加
test
CHANGED
@@ -1,3 +1,7 @@
|
|
1
1
|
Flutter は全く分からないのですが、 Android で「アプリの固有フォルダ」と言えば、そのアプリ以外からはアクセス出来ないと思います。
|
2
2
|
|
3
3
|
仰られるように、 Android はバージョン毎に「公開したいファイルをどこに作ればいいのか」がコロコロ変わっている印象ですので、お使いの Android のバージョンと Flutter でのフォルダ/ディレクトリの得方をしっかりマッチングさせて調べる必要があるように思います。
|
4
|
+
|
5
|
+
(Android10 以前では ) メディアファイル以外のファイルは [ストレージ アクセス フレームワーク](https://developer.android.com/training/data-storage/shared/documents-files?hl=ja) を用いるのが良いかもしれません。
|
6
|
+
Flutter プラグインを探しましたら以下がありました。※ホンモノかどうか私には分かりませんので、一応ご注意ください。
|
7
|
+
[saf: ^1.0.3+3](https://pub.dev/packages/saf)
|
1
修正
test
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
Flutter は全く分からないのですが、 Android で「アプリの固有フォルダ」と言えば、そのアプリ以外からはアクセス出来ないと思います。
|
2
2
|
|
3
|
-
仰られるように、 Android はバージョン毎に公開したいファイルをどこに作ればいいのかがコロコロ変わっている印象ですので、お使いの Android のバージョンと Flutter でのフォルダ/ディレクトリの得方をしっかりマッチングさせて調べる必要があるように思います。
|
3
|
+
仰られるように、 Android はバージョン毎に「公開したいファイルをどこに作ればいいのか」がコロコロ変わっている印象ですので、お使いの Android のバージョンと Flutter でのフォルダ/ディレクトリの得方をしっかりマッチングさせて調べる必要があるように思います。
|