4
4
import android .net .Uri ;
5
5
import android .annotation .TargetApi ;
6
6
import android .app .Activity ;
7
+ import android .content .Context ;
7
8
import android .os .Build ;
8
9
import android .view .KeyEvent ;
9
10
import android .view .View ;
15
16
import android .webkit .WebSettings ;
16
17
import android .webkit .WebView ;
17
18
import android .widget .FrameLayout ;
19
+ import android .provider .MediaStore ;
20
+ import androidx .core .content .FileProvider ;
21
+ import android .database .Cursor ;
22
+ import android .provider .OpenableColumns ;
18
23
24
+ import java .util .List ;
25
+ import java .util .ArrayList ;
19
26
import java .util .HashMap ;
20
27
import java .util .Map ;
28
+ import java .io .File ;
29
+ import java .util .Date ;
30
+ import java .io .IOException ;
31
+ import java .text .SimpleDateFormat ;
21
32
22
33
import io .flutter .plugin .common .MethodCall ;
23
34
import io .flutter .plugin .common .MethodChannel ;
@@ -33,6 +44,15 @@ class WebviewManager {
33
44
private ValueCallback <Uri > mUploadMessage ;
34
45
private ValueCallback <Uri []> mUploadMessageArray ;
35
46
private final static int FILECHOOSER_RESULTCODE =1 ;
47
+ private Uri fileUri ;
48
+ private Uri videoUri ;
49
+
50
+ private long getFileSize (Uri fileUri ) {
51
+ Cursor returnCursor = context .getContentResolver ().query (fileUri , null , null , null , null );
52
+ returnCursor .moveToFirst ();
53
+ int sizeIndex = returnCursor .getColumnIndex (OpenableColumns .SIZE );
54
+ return returnCursor .getLong (sizeIndex );
55
+ }
36
56
37
57
@ TargetApi (7 )
38
58
class ResultHandler {
@@ -41,10 +61,13 @@ public boolean handleResult(int requestCode, int resultCode, Intent intent){
41
61
if (Build .VERSION .SDK_INT >= 21 ){
42
62
if (requestCode == FILECHOOSER_RESULTCODE ){
43
63
Uri [] results = null ;
44
- if (resultCode == Activity .RESULT_OK && intent != null ){
45
- String dataString = intent .getDataString ();
46
- if (dataString != null ){
47
- results = new Uri []{ Uri .parse (dataString ) };
64
+ if (resultCode == Activity .RESULT_OK ) {
65
+ if (fileUri != null && getFileSize (fileUri ) > 0 ) {
66
+ results = new Uri [] { fileUri };
67
+ } else if (videoUri != null && getFileSize (videoUri ) > 0 ) {
68
+ results = new Uri [] { videoUri };
69
+ } else if (intent != null ) {
70
+ results = getSelectedFiles (intent );
48
71
}
49
72
}
50
73
if (mUploadMessageArray != null ){
@@ -70,15 +93,37 @@ public boolean handleResult(int requestCode, int resultCode, Intent intent){
70
93
}
71
94
}
72
95
96
+ private Uri [] getSelectedFiles (Intent data ) {
97
+ // we have one files selected
98
+ if (data .getData () != null ) {
99
+ String dataString = data .getDataString ();
100
+ if (dataString != null ){
101
+ return new Uri []{ Uri .parse (dataString ) };
102
+ }
103
+ }
104
+ // we have multiple files selected
105
+ if (data .getClipData () != null ) {
106
+ final int numSelectedFiles = data .getClipData ().getItemCount ();
107
+ Uri [] result = new Uri [numSelectedFiles ];
108
+ for (int i = 0 ; i < numSelectedFiles ; i ++) {
109
+ result [i ] = data .getClipData ().getItemAt (i ).getUri ();
110
+ }
111
+ return result ;
112
+ }
113
+ return null ;
114
+ }
115
+
73
116
boolean closed = false ;
74
117
WebView webView ;
75
118
Activity activity ;
76
119
BrowserClient webViewClient ;
77
120
ResultHandler resultHandler ;
121
+ Context context ;
78
122
79
- WebviewManager (final Activity activity ) {
123
+ WebviewManager (final Activity activity , final Context context ) {
80
124
this .webView = new ObservableWebView (activity );
81
125
this .activity = activity ;
126
+ this .context = context ;
82
127
this .resultHandler = new ResultHandler ();
83
128
webViewClient = new BrowserClient ();
84
129
webView .setOnKeyListener (new View .OnKeyListener () {
@@ -157,22 +202,109 @@ public boolean onShowFileChooser(
157
202
}
158
203
mUploadMessageArray = filePathCallback ;
159
204
160
- Intent contentSelectionIntent = new Intent (Intent .ACTION_GET_CONTENT );
161
- contentSelectionIntent .addCategory (Intent .CATEGORY_OPENABLE );
162
- contentSelectionIntent .setType ("*/*" );
163
- Intent [] intentArray ;
164
- intentArray = new Intent [0 ];
205
+ final String [] acceptTypes = getSafeAcceptedTypes (fileChooserParams );
206
+ List <Intent > intentList = new ArrayList <Intent >();
207
+ fileUri = null ;
208
+ videoUri = null ;
209
+ if (acceptsImages (acceptTypes )) {
210
+ Intent takePhotoIntent = new Intent (MediaStore .ACTION_IMAGE_CAPTURE );
211
+ fileUri = getOutputFilename (MediaStore .ACTION_IMAGE_CAPTURE );
212
+ takePhotoIntent .putExtra (MediaStore .EXTRA_OUTPUT , fileUri );
213
+ intentList .add (takePhotoIntent );
214
+ }
215
+ if (acceptsVideo (acceptTypes )) {
216
+ Intent takeVideoIntent = new Intent (MediaStore .ACTION_VIDEO_CAPTURE );
217
+ videoUri = getOutputFilename (MediaStore .ACTION_VIDEO_CAPTURE );
218
+ takeVideoIntent .putExtra (MediaStore .EXTRA_OUTPUT , videoUri );
219
+ intentList .add (takeVideoIntent );
220
+ }
221
+ Intent contentSelectionIntent ;
222
+ if (Build .VERSION .SDK_INT >= 21 ) {
223
+ final boolean allowMultiple = fileChooserParams .getMode () == FileChooserParams .MODE_OPEN_MULTIPLE ;
224
+ contentSelectionIntent = fileChooserParams .createIntent ();
225
+ contentSelectionIntent .putExtra (Intent .EXTRA_ALLOW_MULTIPLE , allowMultiple );
226
+ } else {
227
+ contentSelectionIntent = new Intent (Intent .ACTION_GET_CONTENT );
228
+ contentSelectionIntent .addCategory (Intent .CATEGORY_OPENABLE );
229
+ contentSelectionIntent .setType ("*/*" );
230
+ }
231
+ Intent [] intentArray = intentList .toArray (new Intent [intentList .size ()]);
165
232
166
233
Intent chooserIntent = new Intent (Intent .ACTION_CHOOSER );
167
234
chooserIntent .putExtra (Intent .EXTRA_INTENT , contentSelectionIntent );
168
- chooserIntent .putExtra (Intent .EXTRA_TITLE , "Image Chooser" );
169
235
chooserIntent .putExtra (Intent .EXTRA_INITIAL_INTENTS , intentArray );
170
236
activity .startActivityForResult (chooserIntent , FILECHOOSER_RESULTCODE );
171
237
return true ;
172
238
}
173
239
});
174
240
}
175
241
242
+ private Uri getOutputFilename (String intentType ) {
243
+ String prefix = "" ;
244
+ String suffix = "" ;
245
+
246
+ if (intentType == MediaStore .ACTION_IMAGE_CAPTURE ) {
247
+ prefix = "image-" ;
248
+ suffix = ".jpg" ;
249
+ } else if (intentType == MediaStore .ACTION_VIDEO_CAPTURE ) {
250
+ prefix = "video-" ;
251
+ suffix = ".mp4" ;
252
+ }
253
+
254
+ String packageName = context .getPackageName ();
255
+ File capturedFile = null ;
256
+ try {
257
+ capturedFile = createCapturedFile (prefix , suffix );
258
+ } catch (IOException e ) {
259
+ e .printStackTrace ();
260
+ }
261
+ return FileProvider .getUriForFile (context , packageName + ".fileprovider" , capturedFile );
262
+ }
263
+
264
+ private File createCapturedFile (String prefix , String suffix ) throws IOException {
265
+ String timeStamp = new SimpleDateFormat ("yyyyMMdd_HHmmss" ).format (new Date ());
266
+ String imageFileName = prefix + "_" + timeStamp ;
267
+ File storageDir = context .getExternalFilesDir (null );
268
+ return File .createTempFile (imageFileName , suffix , storageDir );
269
+ }
270
+
271
+ private Boolean acceptsImages (String [] types ) {
272
+ return isArrayEmpty (types ) || arrayContainsString (types , "image" );
273
+ }
274
+
275
+ private Boolean acceptsVideo (String [] types ) {
276
+ return isArrayEmpty (types ) || arrayContainsString (types , "video" );
277
+ }
278
+
279
+ private Boolean arrayContainsString (String [] array , String pattern ) {
280
+ for (String content : array ) {
281
+ if (content .contains (pattern )) {
282
+ return true ;
283
+ }
284
+ }
285
+ return false ;
286
+ }
287
+
288
+ private Boolean isArrayEmpty (String [] arr ) {
289
+ // when our array returned from getAcceptTypes() has no values set from the
290
+ // webview
291
+ // i.e. <input type="file" />, without any "accept" attr
292
+ // will be an array with one empty string element, afaik
293
+ return arr .length == 0 || (arr .length == 1 && arr [0 ].length () == 0 );
294
+ }
295
+
296
+ private String [] getSafeAcceptedTypes (WebChromeClient .FileChooserParams params ) {
297
+
298
+ // the getAcceptTypes() is available only in api 21+
299
+ // for lower level, we ignore it
300
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .LOLLIPOP ) {
301
+ return params .getAcceptTypes ();
302
+ }
303
+
304
+ final String [] EMPTY = {};
305
+ return EMPTY ;
306
+ }
307
+
176
308
private void clearCookies () {
177
309
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .LOLLIPOP ) {
178
310
CookieManager .getInstance ().removeAllCookies (new ValueCallback <Boolean >() {
0 commit comments