Skip to content

Commit 2565d18

Browse files
committed
Add ability to choose from camera or gallery when uploading files
some codes from andreipfeiffer/react-native-webview-android-file-upload
1 parent 271e68b commit 2565d18

File tree

5 files changed

+146
-9
lines changed

5 files changed

+146
-9
lines changed

android/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ android {
3333
disable 'InvalidPackage'
3434
}
3535
}
36+
dependencies {
37+
compile "com.android.support:support-v4:22.1.0"
38+
}

android/src/main/AndroidManifest.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:tools="http://schemas.android.com/tools"
23
package="com.flutter_webview_plugin">
4+
<application>
5+
<provider
6+
android:name="android.support.v4.content.FileProvider"
7+
android:authorities="${applicationId}.fileprovider"
8+
android:exported="false"
9+
android:grantUriPermissions="true"
10+
tools:replace="android:authorities">
11+
<meta-data
12+
android:name="android.support.FILE_PROVIDER_PATHS"
13+
android:resource="@xml/filepaths" />
14+
</provider>
15+
</application>
316
</manifest>

android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,20 @@
2424
public class FlutterWebviewPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener {
2525
private Activity activity;
2626
private WebviewManager webViewManager;
27+
private Context context;
2728
static MethodChannel channel;
2829
private static final String CHANNEL_NAME = "flutter_webview_plugin";
2930

3031
public static void registerWith(PluginRegistry.Registrar registrar) {
3132
channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
32-
final FlutterWebviewPlugin instance = new FlutterWebviewPlugin(registrar.activity());
33+
final FlutterWebviewPlugin instance = new FlutterWebviewPlugin(registrar.activity(),registrar.activeContext());
3334
registrar.addActivityResultListener(instance);
3435
channel.setMethodCallHandler(instance);
3536
}
3637

37-
private FlutterWebviewPlugin(Activity activity) {
38+
private FlutterWebviewPlugin(Activity activity, Context context) {
3839
this.activity = activity;
40+
this.context = context;
3941
}
4042

4143
@Override
@@ -100,7 +102,7 @@ private void openUrl(MethodCall call, MethodChannel.Result result) {
100102
boolean geolocationEnabled = call.argument("geolocationEnabled");
101103

102104
if (webViewManager == null || webViewManager.closed == true) {
103-
webViewManager = new WebviewManager(activity);
105+
webViewManager = new WebviewManager(activity, context);
104106
}
105107

106108
FrameLayout.LayoutParams params = buildLayoutParams(call);

android/src/main/java/com/flutter_webview_plugin/WebviewManager.java

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.util.Log;
66
import android.annotation.TargetApi;
77
import android.app.Activity;
8+
import android.content.Context;
89
import android.os.Build;
910
import android.view.KeyEvent;
1011
import android.view.View;
@@ -17,9 +18,17 @@
1718
import android.webkit.WebView;
1819
import android.webkit.WebViewClient;
1920
import android.widget.FrameLayout;
21+
import android.provider.MediaStore;
22+
import android.support.v4.content.FileProvider;
2023

24+
import java.util.List;
25+
import java.util.ArrayList;
2126
import java.util.HashMap;
2227
import java.util.Map;
28+
import java.io.File;
29+
import java.util.Date;
30+
import java.io.IOException;
31+
import java.text.SimpleDateFormat;
2332

2433
import io.flutter.plugin.common.MethodCall;
2534
import io.flutter.plugin.common.MethodChannel;
@@ -35,6 +44,7 @@ class WebviewManager {
3544
private ValueCallback<Uri> mUploadMessage;
3645
private ValueCallback<Uri[]> mUploadMessageArray;
3746
private final static int FILECHOOSER_RESULTCODE=1;
47+
private Uri fileUri;
3848

3949
@TargetApi(7)
4050
class ResultHandler {
@@ -47,6 +57,8 @@ public boolean handleResult(int requestCode, int resultCode, Intent intent){
4757
String dataString = intent.getDataString();
4858
if(dataString != null){
4959
results = new Uri[]{ Uri.parse(dataString) };
60+
}else if(fileUri != null){
61+
results = new Uri[]{ fileUri };
5062
}
5163
}
5264
if(mUploadMessageArray != null){
@@ -76,10 +88,12 @@ public boolean handleResult(int requestCode, int resultCode, Intent intent){
7688
WebView webView;
7789
Activity activity;
7890
ResultHandler resultHandler;
91+
Context context;
7992

80-
WebviewManager(final Activity activity) {
93+
WebviewManager(final Activity activity, final Context context) {
8194
this.webView = new ObservableWebView(activity);
8295
this.activity = activity;
96+
this.context = context;
8397
this.resultHandler = new ResultHandler();
8498
WebViewClient webViewClient = new BrowserClient();
8599
webView.setOnKeyListener(new View.OnKeyListener() {
@@ -158,11 +172,29 @@ public boolean onShowFileChooser(
158172
}
159173
mUploadMessageArray = filePathCallback;
160174

161-
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
162-
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
163-
contentSelectionIntent.setType("*/*");
164-
Intent[] intentArray;
165-
intentArray = new Intent[0];
175+
final String[] acceptTypes = getSafeAcceptedTypes(fileChooserParams);
176+
List<Intent> intentList = new ArrayList<Intent>();
177+
if (acceptsImages(acceptTypes)) {
178+
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
179+
fileUri = getOutputFilename(MediaStore.ACTION_IMAGE_CAPTURE);
180+
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
181+
intentList.add(takePhotoIntent);
182+
}
183+
if (acceptsVideo(acceptTypes)) {
184+
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
185+
fileUri = getOutputFilename(MediaStore.ACTION_VIDEO_CAPTURE);
186+
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
187+
intentList.add(takeVideoIntent);
188+
}
189+
Intent contentSelectionIntent;
190+
if (Build.VERSION.SDK_INT >= 21) {
191+
contentSelectionIntent = fileChooserParams.createIntent();
192+
} else {
193+
contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
194+
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
195+
contentSelectionIntent.setType("*/*");
196+
}
197+
Intent[] intentArray = intentList.toArray(new Intent[intentList.size()]);
166198

167199
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
168200
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
@@ -174,6 +206,73 @@ public boolean onShowFileChooser(
174206
});
175207
}
176208

209+
private Uri getOutputFilename(String intentType) {
210+
String prefix = "";
211+
String suffix = "";
212+
213+
if (intentType == MediaStore.ACTION_IMAGE_CAPTURE) {
214+
prefix = "image-";
215+
suffix = ".jpg";
216+
} else if (intentType == MediaStore.ACTION_VIDEO_CAPTURE) {
217+
prefix = "video-";
218+
suffix = ".mp4";
219+
}
220+
221+
String packageName = context.getPackageName();
222+
File capturedFile = null;
223+
try {
224+
capturedFile = createCapturedFile(prefix, suffix);
225+
} catch (IOException e) {
226+
Log.e("CREATE FILE", "Error occurred while creating the File", e);
227+
e.printStackTrace();
228+
}
229+
return FileProvider.getUriForFile(context, packageName + ".fileprovider", capturedFile);
230+
}
231+
232+
private File createCapturedFile(String prefix, String suffix) throws IOException {
233+
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
234+
String imageFileName = prefix + "_" + timeStamp;
235+
File storageDir = context.getExternalFilesDir(null);
236+
return File.createTempFile(imageFileName, suffix, storageDir);
237+
}
238+
239+
private Boolean acceptsImages(String[] types) {
240+
return isArrayEmpty(types) || arrayContainsString(types, "image");
241+
}
242+
243+
private Boolean acceptsVideo(String[] types) {
244+
return isArrayEmpty(types) || arrayContainsString(types, "video");
245+
}
246+
247+
private Boolean arrayContainsString(String[] array, String pattern) {
248+
for (String content : array) {
249+
if (content.contains(pattern)) {
250+
return true;
251+
}
252+
}
253+
return false;
254+
}
255+
256+
private Boolean isArrayEmpty(String[] arr) {
257+
// when our array returned from getAcceptTypes() has no values set from the
258+
// webview
259+
// i.e. <input type="file" />, without any "accept" attr
260+
// will be an array with one empty string element, afaik
261+
return arr.length == 0 || (arr.length == 1 && arr[0].length() == 0);
262+
}
263+
264+
private String[] getSafeAcceptedTypes(WebChromeClient.FileChooserParams params) {
265+
266+
// the getAcceptTypes() is available only in api 21+
267+
// for lower level, we ignore it
268+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
269+
return params.getAcceptTypes();
270+
}
271+
272+
final String[] EMPTY = {};
273+
return EMPTY;
274+
}
275+
177276
private void clearCookies() {
178277
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
179278
CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<paths>
2+
<external-path
3+
name="external-path"
4+
path="."/>
5+
<external-cache-path
6+
name="external-cache-path"
7+
path="."/>
8+
<external-files-path
9+
name="external-files-path"
10+
path="."/>
11+
<files-path
12+
name="files_path"
13+
path="."/>
14+
<cache-path
15+
name="cache-path"
16+
path="."/>
17+
<root-path
18+
name="name"
19+
path="."/>
20+
</paths>

0 commit comments

Comments
 (0)