From a7a97dbd0a60d438e635b6c4ae55be4dc6bbb41e Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:27:13 +0200 Subject: [PATCH 01/20] Add initial implementation of image chooser for Android devices --- .../plugins/webviewflutter/Constants.java | 14 +++ .../webviewflutter/FileChooserActivity.java | 108 ++++++++++++++++++ .../webviewflutter/FileChooserLauncher.java | 88 ++++++++++++++ .../webviewflutter/GenericFileProvider.java | 6 + .../RequestCameraPermissionActivity.java | 52 +++++++++ .../src/main/res/xml/provider_paths.xml | 4 + .../android/src/main/AndroidManifest.xml | 23 +++- .../webviewflutter/FlutterWebView.java | 10 ++ 8 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java create mode 100644 packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java create mode 100644 packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java create mode 100644 packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java create mode 100644 packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java create mode 100644 packages/webview_flutter/android/src/main/res/xml/provider_paths.xml diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java new file mode 100644 index 000000000000..8c24057a5144 --- /dev/null +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -0,0 +1,14 @@ +package io.flutter.plugins.webviewflutter; + +public class Constants { + public static final String ACTION_PERMISSIONS_GRANTED = "action_permission_granted"; + public static final String ACTION_PERMISSIONS_DENIED = "action_permission_denied"; + + public static final String ACTION_FILE_CHOOSER_FINISHED = "action_file_chooser_completed"; + + public static final String EXTRA_SHOW_CAMERA_OPTION = "extra_show_camera_option"; + public static final String EXTRA_IMAGE_URI = "extra_image_uri"; + + public static final String WEBVIEW_CAMERA_IMAGE_DIRECTORY = "camera_images"; + public static final String WEBVIEW_CAMERA_IMAGE_FILE_NAME = "image.jpg"; +} diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java new file mode 100644 index 000000000000..d7ad4fb0f88e --- /dev/null +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -0,0 +1,108 @@ +package io.flutter.plugins.webviewflutter; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.MediaStore; +import android.util.Log; +import android.view.WindowManager; + +import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; + +import java.io.File; +import java.text.SimpleDateFormat; + +import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_IMAGE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; +import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_CAMERA_IMAGE_DIRECTORY; +import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_CAMERA_IMAGE_FILE_NAME; + +public class FileChooserActivity extends Activity { + + private static final int FILE_CHOOSER_REQUEST_CODE = 12322; + + private Uri cameraImageUri; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + getWindow().setStatusBarColor(Color.TRANSPARENT); + } + + showFileChooser(getIntent().getBooleanExtra(EXTRA_SHOW_CAMERA_OPTION, false)); + } + + private void showFileChooser(boolean enableCamera) { + Intent galleryIntent = createGalleryIntent(); + Intent takePictureIntent = enableCamera ? createCameraIntent() : null; + if (galleryIntent == null && takePictureIntent == null) { + // cannot open anything: cancel file chooser + sendBroadcast(new Intent(ACTION_FILE_CHOOSER_FINISHED)); + finish(); + } else { + Intent[] intentArray = takePictureIntent != null ? new Intent[] { takePictureIntent } : new Intent[]{}; + + Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); + chooserIntent.putExtra(Intent.EXTRA_INTENT, galleryIntent != null ? galleryIntent : takePictureIntent); + chooserIntent.putExtra(Intent.EXTRA_TITLE, "Choose file"); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); + + startActivityForResult(chooserIntent, FILE_CHOOSER_REQUEST_CODE); + } + } + + private Intent createGalleryIntent() { + Intent filesIntent = new Intent(Intent.ACTION_GET_CONTENT); + filesIntent.setType("image/*"); + return (filesIntent.resolveActivity(getPackageManager()) != null) ? filesIntent : null; + } + + private Intent createCameraIntent() { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(getPackageManager()) == null) { + return null; + } + // Create the File where the photo should go + cameraImageUri = getTempImageUri(); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri); + + return takePictureIntent; + } + + private Uri getTempImageUri() { + File imageDirectory = new File(getCacheDir(), WEBVIEW_CAMERA_IMAGE_DIRECTORY); + if (!imageDirectory.exists() && !imageDirectory.mkdir()) { + Log.e("WEBVIEW", "Unable to create image directory"); + } + File imageFile = new File(imageDirectory, WEBVIEW_CAMERA_IMAGE_FILE_NAME); + return FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".generic.provider", imageFile); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == FILE_CHOOSER_REQUEST_CODE) { + Intent intent = new Intent(ACTION_FILE_CHOOSER_FINISHED); + if (resultCode == Activity.RESULT_OK) { + if (data != null && data.getDataString() != null) { + // result from file browser + intent.putExtra(EXTRA_IMAGE_URI, data.getDataString()); + } else { + // result from camera + intent.putExtra(EXTRA_IMAGE_URI, cameraImageUri.toString()); + } + } + sendBroadcast(intent); + finish(); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } +} diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java new file mode 100644 index 000000000000..6ac62a3867c2 --- /dev/null +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -0,0 +1,88 @@ +package io.flutter.plugins.webviewflutter; + +import android.Manifest; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.webkit.ValueCallback; + +import androidx.core.content.ContextCompat; + +import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_PERMISSIONS_DENIED; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_PERMISSIONS_GRANTED; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_IMAGE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; + +public class FileChooserLauncher extends BroadcastReceiver { + + private Activity activity; + private ValueCallback filePathCallback; + + public FileChooserLauncher(Context context, ValueCallback filePathCallback) { + this.activity = getActivityByContext(context); + this.filePathCallback = filePathCallback; + } + + private Activity getActivityByContext(Context context) { + if (context == null) { + return null; + } else if (context instanceof Activity){ + return (Activity)context; + } else if (context instanceof ContextWrapper){ + return getActivityByContext(((ContextWrapper)context).getBaseContext()); + } + + return null; + } + + private boolean hasCameraPermission() { + return ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) + == PackageManager.PERMISSION_GRANTED; + } + + public void start() { + if (hasCameraPermission()) { + showFileChooser(true); + } else { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_PERMISSIONS_GRANTED); + intentFilter.addAction(ACTION_PERMISSIONS_DENIED); + activity.registerReceiver(this, intentFilter); + + activity.startActivity(new Intent(activity, RequestCameraPermissionActivity.class)); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ACTION_PERMISSIONS_GRANTED)) { + activity.unregisterReceiver(this); + showFileChooser(true); + } else if (intent.getAction().equals(ACTION_PERMISSIONS_DENIED)) { + activity.unregisterReceiver(this); + showFileChooser(false); + } else if (intent.getAction().equals(ACTION_FILE_CHOOSER_FINISHED)) { + String uriString = intent.getStringExtra(EXTRA_IMAGE_URI); + System.out.println("!! uriString " + uriString); + Uri[] result = uriString != null ? new Uri[]{Uri.parse(uriString)} : null; + filePathCallback.onReceiveValue(result); + activity.unregisterReceiver(this); + filePathCallback = null; + } + } + + private void showFileChooser(boolean hasCameraPermission) { + IntentFilter intentFilter = new IntentFilter(ACTION_FILE_CHOOSER_FINISHED); + activity.registerReceiver(this, intentFilter); + + Intent intent = new Intent(activity, FileChooserActivity.class); + intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, hasCameraPermission); + activity.startActivity(intent); + } +} \ No newline at end of file diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java new file mode 100644 index 000000000000..c57de71bb2a6 --- /dev/null +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java @@ -0,0 +1,6 @@ +package io.flutter.plugins.webviewflutter; + +import androidx.core.content.FileProvider; + +public class GenericFileProvider extends FileProvider { +} diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java new file mode 100644 index 000000000000..a3615279f20f --- /dev/null +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java @@ -0,0 +1,52 @@ +package io.flutter.plugins.webviewflutter; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; + +import static io.flutter.plugins.webviewflutter.Constants.ACTION_PERMISSIONS_DENIED; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_PERMISSIONS_GRANTED; + +public class RequestCameraPermissionActivity extends Activity { + + private static final int CAMERA_PERMISSION_REQUEST_CODE = 12321; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + getWindow().setStatusBarColor(Color.TRANSPARENT); + } + + ActivityCompat.requestPermissions( + this, + new String[]{Manifest.permission.CAMERA}, + CAMERA_PERMISSION_REQUEST_CODE + ); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + sendBroadcast(new Intent(ACTION_PERMISSIONS_GRANTED)); + } else { + sendBroadcast(new Intent(ACTION_PERMISSIONS_DENIED)); + } + finish(); + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } +} diff --git a/packages/webview_flutter/android/src/main/res/xml/provider_paths.xml b/packages/webview_flutter/android/src/main/res/xml/provider_paths.xml new file mode 100644 index 000000000000..e886703a5364 --- /dev/null +++ b/packages/webview_flutter/android/src/main/res/xml/provider_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml b/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml index a087f2c75c24..37ba6efb9eb7 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml +++ b/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml @@ -1,2 +1,23 @@ - + + + + + + + + + diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index a3b681f27980..cdca76a3c77e 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -7,10 +7,12 @@ import android.annotation.TargetApi; import android.content.Context; import android.hardware.display.DisplayManager; +import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.Message; import android.view.View; +import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebStorage; @@ -63,6 +65,14 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { } return true; } + + @Override + public boolean onShowFileChooser( + WebView webView, ValueCallback filePathCallback, + FileChooserParams fileChooserParams) { + new FileChooserLauncher(webView.getContext(), filePathCallback).start(); + return true; + } }; final WebView newWebView = new WebView(view.getContext()); From d1ba8df05843a439db65df581492a133a7677bf5 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:27:54 +0200 Subject: [PATCH 02/20] Fix incorrect overload --- .../webviewflutter/FlutterWebView.java | 88 +++++++++---------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index cdca76a3c77e..da8d1edf19e8 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -42,38 +42,30 @@ private class FlutterWebChromeClient extends WebChromeClient { @Override public boolean onCreateWindow( - final WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { + final WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { final WebViewClient webViewClient = - new WebViewClient() { - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - @Override - public boolean shouldOverrideUrlLoading( - @NonNull WebView view, @NonNull WebResourceRequest request) { - final String url = request.getUrl().toString(); - if (!flutterWebViewClient.shouldOverrideUrlLoading( - FlutterWebView.this.webView, request)) { - webView.loadUrl(url); - } - return true; - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (!flutterWebViewClient.shouldOverrideUrlLoading( - FlutterWebView.this.webView, url)) { - webView.loadUrl(url); - } - return true; - } - - @Override - public boolean onShowFileChooser( - WebView webView, ValueCallback filePathCallback, - FileChooserParams fileChooserParams) { - new FileChooserLauncher(webView.getContext(), filePathCallback).start(); - return true; - } - }; + new WebViewClient() { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean shouldOverrideUrlLoading( + @NonNull WebView view, @NonNull WebResourceRequest request) { + final String url = request.getUrl().toString(); + if (!flutterWebViewClient.shouldOverrideUrlLoading( + FlutterWebView.this.webView, request)) { + webView.loadUrl(url); + } + return true; + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (!flutterWebViewClient.shouldOverrideUrlLoading( + FlutterWebView.this.webView, url)) { + webView.loadUrl(url); + } + return true; + } + }; final WebView newWebView = new WebView(view.getContext()); newWebView.setWebViewClient(webViewClient); @@ -85,6 +77,12 @@ public boolean onShowFileChooser( return true; } + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { + new FileChooserLauncher(webView.getContext(), filePathCallback).start(); + return true; + } + @Override public void onProgressChanged(WebView view, int progress) { flutterWebViewClient.onLoadingProgress(progress); @@ -94,14 +92,14 @@ public void onProgressChanged(WebView view, int progress) { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @SuppressWarnings("unchecked") FlutterWebView( - final Context context, - MethodChannel methodChannel, - Map params, - View containerView) { + final Context context, + MethodChannel methodChannel, + Map params, + View containerView) { DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy(); DisplayManager displayManager = - (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); + (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); displayListenerProxy.onPreWebViewInitialization(displayManager); webView = @@ -348,13 +346,13 @@ private void evaluateJavaScript(MethodCall methodCall, final Result result) { throw new UnsupportedOperationException("JavaScript string cannot be null"); } webView.evaluateJavascript( - jsString, - new android.webkit.ValueCallback() { - @Override - public void onReceiveValue(String value) { - result.success(value); - } - }); + jsString, + new android.webkit.ValueCallback() { + @Override + public void onReceiveValue(String value) { + result.success(value); + } + }); } @SuppressWarnings("unchecked") @@ -423,7 +421,7 @@ private void applySettings(Map settings) { final boolean hasNavigationDelegate = (boolean) settings.get(key); final WebViewClient webViewClient = - flutterWebViewClient.createWebViewClient(hasNavigationDelegate); + flutterWebViewClient.createWebViewClient(hasNavigationDelegate); webView.setWebViewClient(webViewClient); break; @@ -476,7 +474,7 @@ private void updateAutoMediaPlaybackPolicy(int mode) { private void registerJavaScriptChannelNames(List channelNames) { for (String channelName : channelNames) { webView.addJavascriptInterface( - new JavaScriptChannel(methodChannel, channelName, platformThreadHandler), channelName); + new JavaScriptChannel(methodChannel, channelName, platformThreadHandler), channelName); } } From f09cf2e53f52fe9374179188476690e9064afa41 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:28:22 +0200 Subject: [PATCH 03/20] Fix animating status bar background --- .../plugins/webviewflutter/FileChooserActivity.java | 7 +------ .../plugins/webviewflutter/FileChooserLauncher.java | 3 +-- .../webviewflutter/RequestCameraPermissionActivity.java | 5 ----- .../webview_flutter/android/src/main/AndroidManifest.xml | 6 ++++-- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index d7ad4fb0f88e..7a2d57d587ea 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -8,6 +8,7 @@ import android.os.Bundle; import android.provider.MediaStore; import android.util.Log; +import android.view.Window; import android.view.WindowManager; import androidx.annotation.Nullable; @@ -31,12 +32,6 @@ public class FileChooserActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - getWindow().setStatusBarColor(Color.TRANSPARENT); - } - showFileChooser(getIntent().getBooleanExtra(EXTRA_SHOW_CAMERA_OPTION, false)); } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 6ac62a3867c2..9349d5edd890 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -69,7 +69,6 @@ public void onReceive(Context context, Intent intent) { showFileChooser(false); } else if (intent.getAction().equals(ACTION_FILE_CHOOSER_FINISHED)) { String uriString = intent.getStringExtra(EXTRA_IMAGE_URI); - System.out.println("!! uriString " + uriString); Uri[] result = uriString != null ? new Uri[]{Uri.parse(uriString)} : null; filePathCallback.onReceiveValue(result); activity.unregisterReceiver(this); @@ -85,4 +84,4 @@ private void showFileChooser(boolean hasCameraPermission) { intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, hasCameraPermission); activity.startActivity(intent); } -} \ No newline at end of file +} diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java index a3615279f20f..bc34b35d53fa 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java @@ -24,11 +24,6 @@ public class RequestCameraPermissionActivity extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - getWindow().setStatusBarColor(Color.TRANSPARENT); - } - ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.CAMERA}, diff --git a/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml b/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml index 37ba6efb9eb7..ed4b00699737 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml +++ b/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml @@ -3,10 +3,12 @@ + android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" + android:noHistory="true" /> + android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" + android:noHistory="true" /> Date: Fri, 18 Jun 2021 10:28:44 +0200 Subject: [PATCH 04/20] Use string resources for text and configuraton --- .../plugins/webviewflutter/Constants.java | 16 +++--- .../webviewflutter/FileChooserActivity.java | 13 ++--- .../webviewflutter/FileChooserLauncher.java | 52 +++++++++++-------- .../RequestCameraPermissionActivity.java | 9 +--- .../android/src/main/res/values/strings.xml | 5 ++ .../webviewflutter/FlutterWebView.java | 5 +- 6 files changed, 55 insertions(+), 45 deletions(-) create mode 100644 packages/webview_flutter/android/src/main/res/values/strings.xml diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java index 8c24057a5144..eeb7c22266a0 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -1,14 +1,14 @@ package io.flutter.plugins.webviewflutter; public class Constants { - public static final String ACTION_PERMISSIONS_GRANTED = "action_permission_granted"; - public static final String ACTION_PERMISSIONS_DENIED = "action_permission_denied"; + static final String ACTION_REQUEST_CAMERA_PERMISSION_FINISHED = "action_request_camera_permission_denied"; + static final String ACTION_FILE_CHOOSER_FINISHED = "action_file_chooser_completed"; - public static final String ACTION_FILE_CHOOSER_FINISHED = "action_file_chooser_completed"; + static final String EXTRA_TITLE = "extra_title"; + static final String EXTRA_TYPE = "extra_type"; + static final String EXTRA_SHOW_CAMERA_OPTION = "extra_show_camera_option"; + static final String EXTRA_FILE_URI = "extra_file_uri"; - public static final String EXTRA_SHOW_CAMERA_OPTION = "extra_show_camera_option"; - public static final String EXTRA_IMAGE_URI = "extra_image_uri"; - - public static final String WEBVIEW_CAMERA_IMAGE_DIRECTORY = "camera_images"; - public static final String WEBVIEW_CAMERA_IMAGE_FILE_NAME = "image.jpg"; + static final String WEBVIEW_CAMERA_IMAGE_DIRECTORY = "camera_images"; + static final String WEBVIEW_CAMERA_IMAGE_FILE_NAME = "image.jpg"; } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index 7a2d57d587ea..522ec4b696c6 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -15,10 +15,11 @@ import androidx.core.content.FileProvider; import java.io.File; -import java.text.SimpleDateFormat; import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_IMAGE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_CAMERA_IMAGE_DIRECTORY; import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_CAMERA_IMAGE_FILE_NAME; @@ -47,7 +48,7 @@ private void showFileChooser(boolean enableCamera) { Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); chooserIntent.putExtra(Intent.EXTRA_INTENT, galleryIntent != null ? galleryIntent : takePictureIntent); - chooserIntent.putExtra(Intent.EXTRA_TITLE, "Choose file"); + chooserIntent.putExtra(Intent.EXTRA_TITLE, getIntent().getStringExtra(EXTRA_TITLE)); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); startActivityForResult(chooserIntent, FILE_CHOOSER_REQUEST_CODE); @@ -56,7 +57,7 @@ private void showFileChooser(boolean enableCamera) { private Intent createGalleryIntent() { Intent filesIntent = new Intent(Intent.ACTION_GET_CONTENT); - filesIntent.setType("image/*"); + filesIntent.setType(getIntent().getStringExtra(EXTRA_TYPE)); return (filesIntent.resolveActivity(getPackageManager()) != null) ? filesIntent : null; } @@ -88,10 +89,10 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { if (data != null && data.getDataString() != null) { // result from file browser - intent.putExtra(EXTRA_IMAGE_URI, data.getDataString()); + intent.putExtra(EXTRA_FILE_URI, data.getDataString()); } else { // result from camera - intent.putExtra(EXTRA_IMAGE_URI, cameraImageUri.toString()); + intent.putExtra(EXTRA_FILE_URI, cameraImageUri.toString()); } } sendBroadcast(intent); diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 9349d5edd890..e75d1f5ae765 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -14,18 +14,25 @@ import androidx.core.content.ContextCompat; import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; -import static io.flutter.plugins.webviewflutter.Constants.ACTION_PERMISSIONS_DENIED; -import static io.flutter.plugins.webviewflutter.Constants.ACTION_PERMISSIONS_GRANTED; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_IMAGE_URI; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; public class FileChooserLauncher extends BroadcastReceiver { private Activity activity; + private String title; + private String type; + private boolean showCameraOption; private ValueCallback filePathCallback; - public FileChooserLauncher(Context context, ValueCallback filePathCallback) { + public FileChooserLauncher(Context context, String title, String type, boolean showCameraOption, ValueCallback filePathCallback) { this.activity = getActivityByContext(context); + this.title = title; + this.type = type; + this.showCameraOption = showCameraOption; this.filePathCallback = filePathCallback; } @@ -47,41 +54,40 @@ private boolean hasCameraPermission() { } public void start() { - if (hasCameraPermission()) { - showFileChooser(true); + if (!showCameraOption || hasCameraPermission()) { + showFileChooser(); } else { IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(ACTION_PERMISSIONS_GRANTED); - intentFilter.addAction(ACTION_PERMISSIONS_DENIED); + intentFilter.addAction(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED); activity.registerReceiver(this, intentFilter); activity.startActivity(new Intent(activity, RequestCameraPermissionActivity.class)); } } + private void showFileChooser() { + IntentFilter intentFilter = new IntentFilter(ACTION_FILE_CHOOSER_FINISHED); + activity.registerReceiver(this, intentFilter); + + Intent intent = new Intent(activity, FileChooserActivity.class); + intent.putExtra(EXTRA_TITLE, title); + intent.putExtra(EXTRA_TYPE, type); + intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, showCameraOption && hasCameraPermission()); + activity.startActivity(intent); + } + @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(ACTION_PERMISSIONS_GRANTED)) { - activity.unregisterReceiver(this); - showFileChooser(true); - } else if (intent.getAction().equals(ACTION_PERMISSIONS_DENIED)) { + if (intent.getAction().equals(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED)) { activity.unregisterReceiver(this); - showFileChooser(false); + showFileChooser(); } else if (intent.getAction().equals(ACTION_FILE_CHOOSER_FINISHED)) { - String uriString = intent.getStringExtra(EXTRA_IMAGE_URI); + String uriString = intent.getStringExtra(EXTRA_FILE_URI); + System.out.println("!! finished: " + uriString); Uri[] result = uriString != null ? new Uri[]{Uri.parse(uriString)} : null; filePathCallback.onReceiveValue(result); activity.unregisterReceiver(this); filePathCallback = null; } } - - private void showFileChooser(boolean hasCameraPermission) { - IntentFilter intentFilter = new IntentFilter(ACTION_FILE_CHOOSER_FINISHED); - activity.registerReceiver(this, intentFilter); - - Intent intent = new Intent(activity, FileChooserActivity.class); - intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, hasCameraPermission); - activity.startActivity(intent); - } } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java index bc34b35d53fa..67b4831a0486 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java @@ -13,8 +13,7 @@ import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; -import static io.flutter.plugins.webviewflutter.Constants.ACTION_PERMISSIONS_DENIED; -import static io.flutter.plugins.webviewflutter.Constants.ACTION_PERMISSIONS_GRANTED; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; public class RequestCameraPermissionActivity extends Activity { @@ -34,11 +33,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - sendBroadcast(new Intent(ACTION_PERMISSIONS_GRANTED)); - } else { - sendBroadcast(new Intent(ACTION_PERMISSIONS_DENIED)); - } + sendBroadcast(new Intent(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED)); finish(); } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); diff --git a/packages/webview_flutter/android/src/main/res/values/strings.xml b/packages/webview_flutter/android/src/main/res/values/strings.xml new file mode 100644 index 000000000000..0a6a5f1b4fe1 --- /dev/null +++ b/packages/webview_flutter/android/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Choose a file + image/* + \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index da8d1edf19e8..d26676b68471 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -79,7 +79,10 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { @Override public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { - new FileChooserLauncher(webView.getContext(), filePathCallback).start(); + final Context context = webView.getContext(); + final String title = context.getResources().getString(R.string.webview_file_chooser_title); + final String type = context.getResources().getString(R.string.webview_file_chooser_type); + new FileChooserLauncher(context, title, type, true, filePathCallback).start(); return true; } From 35d9669204de680ae306ab1220b242aff2f4485d Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:29:09 +0200 Subject: [PATCH 05/20] Remove NoHistory attribute from manifest --- .../java/io/flutter/plugins/webviewflutter/Constants.java | 1 - .../plugins/webviewflutter/FileChooserActivity.java | 7 +++++-- .../plugins/webviewflutter/FileChooserLauncher.java | 1 - .../webview_flutter/android/src/main/AndroidManifest.xml | 6 ++---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java index eeb7c22266a0..cb9b01501350 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -10,5 +10,4 @@ public class Constants { static final String EXTRA_FILE_URI = "extra_file_uri"; static final String WEBVIEW_CAMERA_IMAGE_DIRECTORY = "camera_images"; - static final String WEBVIEW_CAMERA_IMAGE_FILE_NAME = "image.jpg"; } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index 522ec4b696c6..3d04b887e2e7 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -15,6 +15,8 @@ import androidx.core.content.FileProvider; import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; @@ -22,11 +24,11 @@ import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_CAMERA_IMAGE_DIRECTORY; -import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_CAMERA_IMAGE_FILE_NAME; public class FileChooserActivity extends Activity { private static final int FILE_CHOOSER_REQUEST_CODE = 12322; + private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); private Uri cameraImageUri; @@ -78,7 +80,8 @@ private Uri getTempImageUri() { if (!imageDirectory.exists() && !imageDirectory.mkdir()) { Log.e("WEBVIEW", "Unable to create image directory"); } - File imageFile = new File(imageDirectory, WEBVIEW_CAMERA_IMAGE_FILE_NAME); + String imageFileName = "IMG_" + simpleDateFormat.format(new Date()) + ".jpg"; + File imageFile = new File(imageDirectory, "camera.jpg"); return FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".generic.provider", imageFile); } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index e75d1f5ae765..7b2c8b6d7918 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -83,7 +83,6 @@ public void onReceive(Context context, Intent intent) { showFileChooser(); } else if (intent.getAction().equals(ACTION_FILE_CHOOSER_FINISHED)) { String uriString = intent.getStringExtra(EXTRA_FILE_URI); - System.out.println("!! finished: " + uriString); Uri[] result = uriString != null ? new Uri[]{Uri.parse(uriString)} : null; filePathCallback.onReceiveValue(result); activity.unregisterReceiver(this); diff --git a/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml b/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml index ed4b00699737..bd8a26f0d7c8 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml +++ b/packages/webview_flutter/webview_flutter/android/src/main/AndroidManifest.xml @@ -3,12 +3,10 @@ + android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" /> + android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" /> Date: Fri, 18 Jun 2021 10:29:29 +0200 Subject: [PATCH 06/20] Copy external image to local storage as the Uri returned from the chooser intent is only available for the calling context --- .../plugins/webviewflutter/Constants.java | 2 +- .../webviewflutter/FileChooserActivity.java | 81 ++++++++++++++++--- .../webviewflutter/FileChooserLauncher.java | 2 +- .../src/main/res/xml/provider_paths.xml | 2 +- 4 files changed, 72 insertions(+), 15 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java index cb9b01501350..24ae4c787009 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -9,5 +9,5 @@ public class Constants { static final String EXTRA_SHOW_CAMERA_OPTION = "extra_show_camera_option"; static final String EXTRA_FILE_URI = "extra_file_uri"; - static final String WEBVIEW_CAMERA_IMAGE_DIRECTORY = "camera_images"; + static final String WEBVIEW_STORAGE_DIRECTORY = "storage"; } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index 3d04b887e2e7..95ac6a4c2a6e 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -2,28 +2,30 @@ import android.app.Activity; import android.content.Intent; -import android.graphics.Color; +import android.database.Cursor; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; +import android.provider.OpenableColumns; import android.util.Log; -import android.view.Window; -import android.view.WindowManager; import androidx.annotation.Nullable; import androidx.core.content.FileProvider; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; -import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_CAMERA_IMAGE_DIRECTORY; +import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_STORAGE_DIRECTORY; public class FileChooserActivity extends Activity { @@ -75,16 +77,69 @@ private Intent createCameraIntent() { return takePictureIntent; } - private Uri getTempImageUri() { - File imageDirectory = new File(getCacheDir(), WEBVIEW_CAMERA_IMAGE_DIRECTORY); + private File getStorageDirectory() { + File imageDirectory = new File(getCacheDir(), WEBVIEW_STORAGE_DIRECTORY); if (!imageDirectory.exists() && !imageDirectory.mkdir()) { - Log.e("WEBVIEW", "Unable to create image directory"); + Log.e("WEBVIEW", "Unable to create storage directory"); } - String imageFileName = "IMG_" + simpleDateFormat.format(new Date()) + ".jpg"; - File imageFile = new File(imageDirectory, "camera.jpg"); + return imageDirectory; + } + + private Uri getTempImageUri() { + String imageFileName = "IMG-" + simpleDateFormat.format(new Date()) + ".jpg"; + File imageFile = new File(getStorageDirectory(), imageFileName); return FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".generic.provider", imageFile); } + private String getFileNameFromUri(Uri uri) { + Cursor returnCursor = getContentResolver().query(uri, null, null, null, null); + assert returnCursor != null; + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + returnCursor.moveToFirst(); + String name = returnCursor.getString(nameIndex); + returnCursor.close(); + return name; + } + + private Uri copyToLocalUri(Uri uri) { + InputStream in = null; + OutputStream out = null; + try { + File destination = new File(getStorageDirectory(), getFileNameFromUri(uri)); + in = getContentResolver().openInputStream(uri); + out = new FileOutputStream(destination); + + int cnt = 0; + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + cnt += len; + } + + return FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".generic.provider", destination); + } catch(Exception e){ + Log.e("WEBVIEW", "Unable to copy selected image", e); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (out != null) { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + return null; + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == FILE_CHOOSER_REQUEST_CODE) { @@ -92,7 +147,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { if (data != null && data.getDataString() != null) { // result from file browser - intent.putExtra(EXTRA_FILE_URI, data.getDataString()); + final Uri uri = copyToLocalUri(data.getData()); + intent.putExtra(EXTRA_FILE_URI, uri.toString()); } else { // result from camera intent.putExtra(EXTRA_FILE_URI, cameraImageUri.toString()); @@ -104,4 +160,5 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); } } + } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 7b2c8b6d7918..d6ffc2305ee4 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -16,9 +16,9 @@ import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; public class FileChooserLauncher extends BroadcastReceiver { diff --git a/packages/webview_flutter/android/src/main/res/xml/provider_paths.xml b/packages/webview_flutter/android/src/main/res/xml/provider_paths.xml index e886703a5364..d1162a4fd81b 100644 --- a/packages/webview_flutter/android/src/main/res/xml/provider_paths.xml +++ b/packages/webview_flutter/android/src/main/res/xml/provider_paths.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file From 905ccda7b0afd2935c94c67c5de6ce6a9d661fd3 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:29:50 +0200 Subject: [PATCH 07/20] Update doc files --- packages/webview_flutter/webview_flutter/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 361bfd24f3af..010092ef2dcc 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Add file chooser on Android * Updated Android lint settings. ## 2.0.12 From 3c61045e033b80107d281e055252adf5da27db2f Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:30:13 +0200 Subject: [PATCH 08/20] Add file headers --- .../java/io/flutter/plugins/webviewflutter/Constants.java | 4 ++++ .../flutter/plugins/webviewflutter/FileChooserActivity.java | 4 ++++ .../flutter/plugins/webviewflutter/FileChooserLauncher.java | 4 ++++ .../flutter/plugins/webviewflutter/GenericFileProvider.java | 4 ++++ .../webviewflutter/RequestCameraPermissionActivity.java | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java index 24ae4c787009..78652be61a72 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -1,3 +1,7 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + package io.flutter.plugins.webviewflutter; public class Constants { diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index 95ac6a4c2a6e..7f4d25926504 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -1,3 +1,7 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + package io.flutter.plugins.webviewflutter; import android.app.Activity; diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index d6ffc2305ee4..d73dc332803b 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -1,3 +1,7 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + package io.flutter.plugins.webviewflutter; import android.Manifest; diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java index c57de71bb2a6..1af7f1284140 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java @@ -1,3 +1,7 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + package io.flutter.plugins.webviewflutter; import androidx.core.content.FileProvider; diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java index 67b4831a0486..f62c7bcda7ca 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java @@ -1,3 +1,7 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + package io.flutter.plugins.webviewflutter; import android.Manifest; From 70f1d376ac5e187f0b3caa5b3c102fe7860ab63d Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:30:42 +0200 Subject: [PATCH 09/20] Fix java file formatting --- .../plugins/webviewflutter/Constants.java | 15 +- .../webviewflutter/FileChooserActivity.java | 257 +++++++++--------- .../webviewflutter/FileChooserLauncher.java | 132 ++++----- .../webviewflutter/GenericFileProvider.java | 3 +- .../RequestCameraPermissionActivity.java | 43 ++- .../webviewflutter/FlutterWebView.java | 79 +++--- 6 files changed, 265 insertions(+), 264 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java index 78652be61a72..31bc0b18e7f4 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -5,13 +5,14 @@ package io.flutter.plugins.webviewflutter; public class Constants { - static final String ACTION_REQUEST_CAMERA_PERMISSION_FINISHED = "action_request_camera_permission_denied"; - static final String ACTION_FILE_CHOOSER_FINISHED = "action_file_chooser_completed"; + static final String ACTION_REQUEST_CAMERA_PERMISSION_FINISHED = + "action_request_camera_permission_denied"; + static final String ACTION_FILE_CHOOSER_FINISHED = "action_file_chooser_completed"; - static final String EXTRA_TITLE = "extra_title"; - static final String EXTRA_TYPE = "extra_type"; - static final String EXTRA_SHOW_CAMERA_OPTION = "extra_show_camera_option"; - static final String EXTRA_FILE_URI = "extra_file_uri"; + static final String EXTRA_TITLE = "extra_title"; + static final String EXTRA_TYPE = "extra_type"; + static final String EXTRA_SHOW_CAMERA_OPTION = "extra_show_camera_option"; + static final String EXTRA_FILE_URI = "extra_file_uri"; - static final String WEBVIEW_STORAGE_DIRECTORY = "storage"; + static final String WEBVIEW_STORAGE_DIRECTORY = "storage"; } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index 7f4d25926504..6bb0a83c9d2f 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -4,6 +4,13 @@ package io.flutter.plugins.webviewflutter; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; +import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_STORAGE_DIRECTORY; + import android.app.Activity; import android.content.Intent; import android.database.Cursor; @@ -12,10 +19,8 @@ import android.provider.MediaStore; import android.provider.OpenableColumns; import android.util.Log; - import androidx.annotation.Nullable; import androidx.core.content.FileProvider; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -24,145 +29,141 @@ import java.text.SimpleDateFormat; import java.util.Date; -import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; -import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_STORAGE_DIRECTORY; - public class FileChooserActivity extends Activity { - private static final int FILE_CHOOSER_REQUEST_CODE = 12322; - private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); - - private Uri cameraImageUri; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - showFileChooser(getIntent().getBooleanExtra(EXTRA_SHOW_CAMERA_OPTION, false)); + private static final int FILE_CHOOSER_REQUEST_CODE = 12322; + private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); + + private Uri cameraImageUri; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + showFileChooser(getIntent().getBooleanExtra(EXTRA_SHOW_CAMERA_OPTION, false)); + } + + private void showFileChooser(boolean enableCamera) { + Intent galleryIntent = createGalleryIntent(); + Intent takePictureIntent = enableCamera ? createCameraIntent() : null; + if (galleryIntent == null && takePictureIntent == null) { + // cannot open anything: cancel file chooser + sendBroadcast(new Intent(ACTION_FILE_CHOOSER_FINISHED)); + finish(); + } else { + Intent[] intentArray = + takePictureIntent != null ? new Intent[] {takePictureIntent} : new Intent[] {}; + + Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); + chooserIntent.putExtra( + Intent.EXTRA_INTENT, galleryIntent != null ? galleryIntent : takePictureIntent); + chooserIntent.putExtra(Intent.EXTRA_TITLE, getIntent().getStringExtra(EXTRA_TITLE)); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); + + startActivityForResult(chooserIntent, FILE_CHOOSER_REQUEST_CODE); } - - private void showFileChooser(boolean enableCamera) { - Intent galleryIntent = createGalleryIntent(); - Intent takePictureIntent = enableCamera ? createCameraIntent() : null; - if (galleryIntent == null && takePictureIntent == null) { - // cannot open anything: cancel file chooser - sendBroadcast(new Intent(ACTION_FILE_CHOOSER_FINISHED)); - finish(); - } else { - Intent[] intentArray = takePictureIntent != null ? new Intent[] { takePictureIntent } : new Intent[]{}; - - Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); - chooserIntent.putExtra(Intent.EXTRA_INTENT, galleryIntent != null ? galleryIntent : takePictureIntent); - chooserIntent.putExtra(Intent.EXTRA_TITLE, getIntent().getStringExtra(EXTRA_TITLE)); - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); - - startActivityForResult(chooserIntent, FILE_CHOOSER_REQUEST_CODE); - } + } + + private Intent createGalleryIntent() { + Intent filesIntent = new Intent(Intent.ACTION_GET_CONTENT); + filesIntent.setType(getIntent().getStringExtra(EXTRA_TYPE)); + return (filesIntent.resolveActivity(getPackageManager()) != null) ? filesIntent : null; + } + + private Intent createCameraIntent() { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(getPackageManager()) == null) { + return null; } + // Create the File where the photo should go + cameraImageUri = getTempImageUri(); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri); - private Intent createGalleryIntent() { - Intent filesIntent = new Intent(Intent.ACTION_GET_CONTENT); - filesIntent.setType(getIntent().getStringExtra(EXTRA_TYPE)); - return (filesIntent.resolveActivity(getPackageManager()) != null) ? filesIntent : null; - } - - private Intent createCameraIntent() { - Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - if (takePictureIntent.resolveActivity(getPackageManager()) == null) { - return null; - } - // Create the File where the photo should go - cameraImageUri = getTempImageUri(); - takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri); + return takePictureIntent; + } - return takePictureIntent; + private File getStorageDirectory() { + File imageDirectory = new File(getCacheDir(), WEBVIEW_STORAGE_DIRECTORY); + if (!imageDirectory.exists() && !imageDirectory.mkdir()) { + Log.e("WEBVIEW", "Unable to create storage directory"); } - - private File getStorageDirectory() { - File imageDirectory = new File(getCacheDir(), WEBVIEW_STORAGE_DIRECTORY); - if (!imageDirectory.exists() && !imageDirectory.mkdir()) { - Log.e("WEBVIEW", "Unable to create storage directory"); + return imageDirectory; + } + + private Uri getTempImageUri() { + String imageFileName = "IMG-" + simpleDateFormat.format(new Date()) + ".jpg"; + File imageFile = new File(getStorageDirectory(), imageFileName); + return FileProvider.getUriForFile( + this, getApplicationContext().getPackageName() + ".generic.provider", imageFile); + } + + private String getFileNameFromUri(Uri uri) { + Cursor returnCursor = getContentResolver().query(uri, null, null, null, null); + assert returnCursor != null; + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + returnCursor.moveToFirst(); + String name = returnCursor.getString(nameIndex); + returnCursor.close(); + return name; + } + + private Uri copyToLocalUri(Uri uri) { + InputStream in = null; + OutputStream out = null; + try { + File destination = new File(getStorageDirectory(), getFileNameFromUri(uri)); + in = getContentResolver().openInputStream(uri); + out = new FileOutputStream(destination); + + int cnt = 0; + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + cnt += len; + } + + return FileProvider.getUriForFile( + this, getApplicationContext().getPackageName() + ".generic.provider", destination); + } catch (Exception e) { + Log.e("WEBVIEW", "Unable to copy selected image", e); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); } - return imageDirectory; - } - - private Uri getTempImageUri() { - String imageFileName = "IMG-" + simpleDateFormat.format(new Date()) + ".jpg"; - File imageFile = new File(getStorageDirectory(), imageFileName); - return FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".generic.provider", imageFile); - } - - private String getFileNameFromUri(Uri uri) { - Cursor returnCursor = getContentResolver().query(uri, null, null, null, null); - assert returnCursor != null; - int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); - returnCursor.moveToFirst(); - String name = returnCursor.getString(nameIndex); - returnCursor.close(); - return name; - } - - private Uri copyToLocalUri(Uri uri) { - InputStream in = null; - OutputStream out = null; + } + if (out != null) { try { - File destination = new File(getStorageDirectory(), getFileNameFromUri(uri)); - in = getContentResolver().openInputStream(uri); - out = new FileOutputStream(destination); - - int cnt = 0; - byte[] buffer = new byte[1024]; - int len; - while ((len = in.read(buffer)) != -1) { - out.write(buffer, 0, len); - cnt += len; - } - - return FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".generic.provider", destination); - } catch(Exception e){ - Log.e("WEBVIEW", "Unable to copy selected image", e); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (out != null) { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } + out.close(); + } catch (IOException e) { + e.printStackTrace(); } - - return null; + } } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == FILE_CHOOSER_REQUEST_CODE) { - Intent intent = new Intent(ACTION_FILE_CHOOSER_FINISHED); - if (resultCode == Activity.RESULT_OK) { - if (data != null && data.getDataString() != null) { - // result from file browser - final Uri uri = copyToLocalUri(data.getData()); - intent.putExtra(EXTRA_FILE_URI, uri.toString()); - } else { - // result from camera - intent.putExtra(EXTRA_FILE_URI, cameraImageUri.toString()); - } - } - sendBroadcast(intent); - finish(); + return null; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == FILE_CHOOSER_REQUEST_CODE) { + Intent intent = new Intent(ACTION_FILE_CHOOSER_FINISHED); + if (resultCode == Activity.RESULT_OK) { + if (data != null && data.getDataString() != null) { + // result from file browser + final Uri uri = copyToLocalUri(data.getData()); + intent.putExtra(EXTRA_FILE_URI, uri.toString()); } else { - super.onActivityResult(requestCode, resultCode, data); + // result from camera + intent.putExtra(EXTRA_FILE_URI, cameraImageUri.toString()); } + } + sendBroadcast(intent); + finish(); + } else { + super.onActivityResult(requestCode, resultCode, data); } - + } } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index d73dc332803b..7e00477aca0a 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -4,6 +4,13 @@ package io.flutter.plugins.webviewflutter; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; + import android.Manifest; import android.app.Activity; import android.content.BroadcastReceiver; @@ -14,83 +21,80 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.webkit.ValueCallback; - import androidx.core.content.ContextCompat; -import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; -import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; - public class FileChooserLauncher extends BroadcastReceiver { - private Activity activity; - private String title; - private String type; - private boolean showCameraOption; - private ValueCallback filePathCallback; + private Activity activity; + private String title; + private String type; + private boolean showCameraOption; + private ValueCallback filePathCallback; - public FileChooserLauncher(Context context, String title, String type, boolean showCameraOption, ValueCallback filePathCallback) { - this.activity = getActivityByContext(context); - this.title = title; - this.type = type; - this.showCameraOption = showCameraOption; - this.filePathCallback = filePathCallback; - } - - private Activity getActivityByContext(Context context) { - if (context == null) { - return null; - } else if (context instanceof Activity){ - return (Activity)context; - } else if (context instanceof ContextWrapper){ - return getActivityByContext(((ContextWrapper)context).getBaseContext()); - } + public FileChooserLauncher( + Context context, + String title, + String type, + boolean showCameraOption, + ValueCallback filePathCallback) { + this.activity = getActivityByContext(context); + this.title = title; + this.type = type; + this.showCameraOption = showCameraOption; + this.filePathCallback = filePathCallback; + } - return null; + private Activity getActivityByContext(Context context) { + if (context == null) { + return null; + } else if (context instanceof Activity) { + return (Activity) context; + } else if (context instanceof ContextWrapper) { + return getActivityByContext(((ContextWrapper) context).getBaseContext()); } - private boolean hasCameraPermission() { - return ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) - == PackageManager.PERMISSION_GRANTED; - } + return null; + } - public void start() { - if (!showCameraOption || hasCameraPermission()) { - showFileChooser(); - } else { - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED); - activity.registerReceiver(this, intentFilter); + private boolean hasCameraPermission() { + return ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) + == PackageManager.PERMISSION_GRANTED; + } - activity.startActivity(new Intent(activity, RequestCameraPermissionActivity.class)); - } + public void start() { + if (!showCameraOption || hasCameraPermission()) { + showFileChooser(); + } else { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED); + activity.registerReceiver(this, intentFilter); + + activity.startActivity(new Intent(activity, RequestCameraPermissionActivity.class)); } + } - private void showFileChooser() { - IntentFilter intentFilter = new IntentFilter(ACTION_FILE_CHOOSER_FINISHED); - activity.registerReceiver(this, intentFilter); + private void showFileChooser() { + IntentFilter intentFilter = new IntentFilter(ACTION_FILE_CHOOSER_FINISHED); + activity.registerReceiver(this, intentFilter); - Intent intent = new Intent(activity, FileChooserActivity.class); - intent.putExtra(EXTRA_TITLE, title); - intent.putExtra(EXTRA_TYPE, type); - intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, showCameraOption && hasCameraPermission()); - activity.startActivity(intent); - } + Intent intent = new Intent(activity, FileChooserActivity.class); + intent.putExtra(EXTRA_TITLE, title); + intent.putExtra(EXTRA_TYPE, type); + intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, showCameraOption && hasCameraPermission()); + activity.startActivity(intent); + } - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED)) { - activity.unregisterReceiver(this); - showFileChooser(); - } else if (intent.getAction().equals(ACTION_FILE_CHOOSER_FINISHED)) { - String uriString = intent.getStringExtra(EXTRA_FILE_URI); - Uri[] result = uriString != null ? new Uri[]{Uri.parse(uriString)} : null; - filePathCallback.onReceiveValue(result); - activity.unregisterReceiver(this); - filePathCallback = null; - } + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED)) { + activity.unregisterReceiver(this); + showFileChooser(); + } else if (intent.getAction().equals(ACTION_FILE_CHOOSER_FINISHED)) { + String uriString = intent.getStringExtra(EXTRA_FILE_URI); + Uri[] result = uriString != null ? new Uri[] {Uri.parse(uriString)} : null; + filePathCallback.onReceiveValue(result); + activity.unregisterReceiver(this); + filePathCallback = null; } + } } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java index 1af7f1284140..3b9a218c7d7a 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java @@ -6,5 +6,4 @@ import androidx.core.content.FileProvider; -public class GenericFileProvider extends FileProvider { -} +public class GenericFileProvider extends FileProvider {} diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java index f62c7bcda7ca..de79f09dbde4 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java @@ -4,43 +4,36 @@ package io.flutter.plugins.webviewflutter; +import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; + import android.Manifest; import android.app.Activity; import android.content.Intent; -import android.content.pm.PackageManager; -import android.graphics.Color; -import android.os.Build; import android.os.Bundle; -import android.view.WindowManager; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; -import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; - public class RequestCameraPermissionActivity extends Activity { - private static final int CAMERA_PERMISSION_REQUEST_CODE = 12321; + private static final int CAMERA_PERMISSION_REQUEST_CODE = 12321; - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - ActivityCompat.requestPermissions( - this, - new String[]{Manifest.permission.CAMERA}, - CAMERA_PERMISSION_REQUEST_CODE - ); - } + ActivityCompat.requestPermissions( + this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); + } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) { - sendBroadcast(new Intent(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED)); - finish(); - } else { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - } + @Override + public void onRequestPermissionsResult( + int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) { + sendBroadcast(new Intent(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED)); + finish(); + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); } + } } diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index d26676b68471..b358e07b1c2d 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -42,30 +42,30 @@ private class FlutterWebChromeClient extends WebChromeClient { @Override public boolean onCreateWindow( - final WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { + final WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { final WebViewClient webViewClient = - new WebViewClient() { - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - @Override - public boolean shouldOverrideUrlLoading( - @NonNull WebView view, @NonNull WebResourceRequest request) { - final String url = request.getUrl().toString(); - if (!flutterWebViewClient.shouldOverrideUrlLoading( - FlutterWebView.this.webView, request)) { - webView.loadUrl(url); - } - return true; - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (!flutterWebViewClient.shouldOverrideUrlLoading( - FlutterWebView.this.webView, url)) { - webView.loadUrl(url); - } - return true; - } - }; + new WebViewClient() { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean shouldOverrideUrlLoading( + @NonNull WebView view, @NonNull WebResourceRequest request) { + final String url = request.getUrl().toString(); + if (!flutterWebViewClient.shouldOverrideUrlLoading( + FlutterWebView.this.webView, request)) { + webView.loadUrl(url); + } + return true; + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (!flutterWebViewClient.shouldOverrideUrlLoading( + FlutterWebView.this.webView, url)) { + webView.loadUrl(url); + } + return true; + } + }; final WebView newWebView = new WebView(view.getContext()); newWebView.setWebViewClient(webViewClient); @@ -78,7 +78,10 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { } @Override - public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { + public boolean onShowFileChooser( + WebView webView, + ValueCallback filePathCallback, + FileChooserParams fileChooserParams) { final Context context = webView.getContext(); final String title = context.getResources().getString(R.string.webview_file_chooser_title); final String type = context.getResources().getString(R.string.webview_file_chooser_type); @@ -95,14 +98,14 @@ public void onProgressChanged(WebView view, int progress) { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @SuppressWarnings("unchecked") FlutterWebView( - final Context context, - MethodChannel methodChannel, - Map params, - View containerView) { + final Context context, + MethodChannel methodChannel, + Map params, + View containerView) { DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy(); DisplayManager displayManager = - (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); + (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); displayListenerProxy.onPreWebViewInitialization(displayManager); webView = @@ -349,13 +352,13 @@ private void evaluateJavaScript(MethodCall methodCall, final Result result) { throw new UnsupportedOperationException("JavaScript string cannot be null"); } webView.evaluateJavascript( - jsString, - new android.webkit.ValueCallback() { - @Override - public void onReceiveValue(String value) { - result.success(value); - } - }); + jsString, + new android.webkit.ValueCallback() { + @Override + public void onReceiveValue(String value) { + result.success(value); + } + }); } @SuppressWarnings("unchecked") @@ -424,7 +427,7 @@ private void applySettings(Map settings) { final boolean hasNavigationDelegate = (boolean) settings.get(key); final WebViewClient webViewClient = - flutterWebViewClient.createWebViewClient(hasNavigationDelegate); + flutterWebViewClient.createWebViewClient(hasNavigationDelegate); webView.setWebViewClient(webViewClient); break; @@ -477,7 +480,7 @@ private void updateAutoMediaPlaybackPolicy(int mode) { private void registerJavaScriptChannelNames(List channelNames) { for (String channelName : channelNames) { webView.addJavascriptInterface( - new JavaScriptChannel(methodChannel, channelName, platformThreadHandler), channelName); + new JavaScriptChannel(methodChannel, channelName, platformThreadHandler), channelName); } } From af0a29b68eaaa866cc5c0baf0f89452f465e7d10 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:31:29 +0200 Subject: [PATCH 10/20] Remove redundant conversion from context to activity --- .../webviewflutter/FileChooserLauncher.java | 45 +++++++------------ 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 7e00477aca0a..e7661d9939ec 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -12,7 +12,6 @@ import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; import android.Manifest; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -25,40 +24,28 @@ public class FileChooserLauncher extends BroadcastReceiver { - private Activity activity; + private Context context; private String title; private String type; private boolean showCameraOption; private ValueCallback filePathCallback; public FileChooserLauncher( - Context context, - String title, - String type, - boolean showCameraOption, - ValueCallback filePathCallback) { - this.activity = getActivityByContext(context); + Context context, + String title, + String type, + boolean showCameraOption, + ValueCallback filePathCallback) { + this.context = context; this.title = title; this.type = type; this.showCameraOption = showCameraOption; this.filePathCallback = filePathCallback; } - private Activity getActivityByContext(Context context) { - if (context == null) { - return null; - } else if (context instanceof Activity) { - return (Activity) context; - } else if (context instanceof ContextWrapper) { - return getActivityByContext(((ContextWrapper) context).getBaseContext()); - } - - return null; - } - private boolean hasCameraPermission() { - return ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) - == PackageManager.PERMISSION_GRANTED; + return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) + == PackageManager.PERMISSION_GRANTED; } public void start() { @@ -67,33 +54,33 @@ public void start() { } else { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED); - activity.registerReceiver(this, intentFilter); + context.registerReceiver(this, intentFilter); - activity.startActivity(new Intent(activity, RequestCameraPermissionActivity.class)); + context.startActivity(new Intent(context, RequestCameraPermissionActivity.class)); } } private void showFileChooser() { IntentFilter intentFilter = new IntentFilter(ACTION_FILE_CHOOSER_FINISHED); - activity.registerReceiver(this, intentFilter); + context.registerReceiver(this, intentFilter); - Intent intent = new Intent(activity, FileChooserActivity.class); + Intent intent = new Intent(context, FileChooserActivity.class); intent.putExtra(EXTRA_TITLE, title); intent.putExtra(EXTRA_TYPE, type); intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, showCameraOption && hasCameraPermission()); - activity.startActivity(intent); + context.startActivity(intent); } @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED)) { - activity.unregisterReceiver(this); + context.unregisterReceiver(this); showFileChooser(); } else if (intent.getAction().equals(ACTION_FILE_CHOOSER_FINISHED)) { String uriString = intent.getStringExtra(EXTRA_FILE_URI); Uri[] result = uriString != null ? new Uri[] {Uri.parse(uriString)} : null; filePathCallback.onReceiveValue(result); - activity.unregisterReceiver(this); + context.unregisterReceiver(this); filePathCallback = null; } } From a5b6312a8993030fd52c57f6ea906017a22e5872 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:31:47 +0200 Subject: [PATCH 11/20] Fix formatting --- .../plugins/webviewflutter/FileChooserLauncher.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index e7661d9939ec..2905871e6734 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -14,7 +14,6 @@ import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; @@ -31,11 +30,11 @@ public class FileChooserLauncher extends BroadcastReceiver { private ValueCallback filePathCallback; public FileChooserLauncher( - Context context, - String title, - String type, - boolean showCameraOption, - ValueCallback filePathCallback) { + Context context, + String title, + String type, + boolean showCameraOption, + ValueCallback filePathCallback) { this.context = context; this.title = title; this.type = type; @@ -45,7 +44,7 @@ public FileChooserLauncher( private boolean hasCameraPermission() { return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) - == PackageManager.PERMISSION_GRANTED; + == PackageManager.PERMISSION_GRANTED; } public void start() { From a9bf05dfbb4de0fc36cec226f554eaadb9138ed6 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:32:14 +0200 Subject: [PATCH 12/20] Add flag to intent to be able to launch it from a non-activity context --- .../flutter/plugins/webviewflutter/FileChooserLauncher.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 2905871e6734..5e60fc661030 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -55,7 +55,9 @@ public void start() { intentFilter.addAction(ACTION_REQUEST_CAMERA_PERMISSION_FINISHED); context.registerReceiver(this, intentFilter); - context.startActivity(new Intent(context, RequestCameraPermissionActivity.class)); + Intent intent = new Intent(context, RequestCameraPermissionActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); } } @@ -67,6 +69,7 @@ private void showFileChooser() { intent.putExtra(EXTRA_TITLE, title); intent.putExtra(EXTRA_TYPE, type); intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, showCameraOption && hasCameraPermission()); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } From 926a1ab6b20232dc549c17c3bf8d825a3b7166e1 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:32:42 +0200 Subject: [PATCH 13/20] Allow upload of multiple files --- .../plugins/webviewflutter/Constants.java | 10 +- .../webviewflutter/FileChooserActivity.java | 173 +++++++++++------- .../webviewflutter/FileChooserLauncher.java | 87 +++++++-- .../android/src/main/res/values/strings.xml | 3 +- .../webviewflutter/FlutterWebView.java | 12 +- 5 files changed, 190 insertions(+), 95 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java index 31bc0b18e7f4..cc884fc44a29 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -10,9 +10,11 @@ public class Constants { static final String ACTION_FILE_CHOOSER_FINISHED = "action_file_chooser_completed"; static final String EXTRA_TITLE = "extra_title"; - static final String EXTRA_TYPE = "extra_type"; - static final String EXTRA_SHOW_CAMERA_OPTION = "extra_show_camera_option"; - static final String EXTRA_FILE_URI = "extra_file_uri"; + static final String EXTRA_ACCEPT_TYPES = "extra_types"; + static final String EXTRA_SHOW_VIDEO_OPTION = "extra_show_video_option"; + static final String EXTRA_SHOW_IMAGE_OPTION = "extra_show_image_option"; + static final String EXTRA_FILE_URIS = "extra_file_uris"; + static final String EXTRA_ALLOW_MULTIPLE_FILES = "extra_allow_multiple_files"; static final String WEBVIEW_STORAGE_DIRECTORY = "storage"; -} +} \ No newline at end of file diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index 6bb0a83c9d2f..a1c82a926ca9 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -5,10 +5,12 @@ package io.flutter.plugins.webviewflutter; import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ALLOW_MULTIPLE_FILES; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URIS; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_IMAGE_OPTION; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_VIDEO_OPTION; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ACCEPT_TYPES; import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_STORAGE_DIRECTORY; import android.app.Activity; @@ -27,6 +29,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; public class FileChooserActivity extends Activity { @@ -34,51 +37,87 @@ public class FileChooserActivity extends Activity { private static final int FILE_CHOOSER_REQUEST_CODE = 12322; private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); - private Uri cameraImageUri; + // List of Uris that point to files where there MIGHT be the output of the capture. At most one of these can be valid + private final ArrayList potentialCaptureOutputUris = new ArrayList<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - showFileChooser(getIntent().getBooleanExtra(EXTRA_SHOW_CAMERA_OPTION, false)); + showFileChooser(getIntent().getBooleanExtra(EXTRA_SHOW_IMAGE_OPTION, false), getIntent().getBooleanExtra(EXTRA_SHOW_VIDEO_OPTION, false)); } - private void showFileChooser(boolean enableCamera) { - Intent galleryIntent = createGalleryIntent(); - Intent takePictureIntent = enableCamera ? createCameraIntent() : null; - if (galleryIntent == null && takePictureIntent == null) { + private void showFileChooser(boolean showImageIntent, boolean showVideoIntent) { + Intent getContentIntent = createGetContentIntent(); + Intent captureImageIntent = showImageIntent ? createCaptureIntent(MediaStore.ACTION_IMAGE_CAPTURE, "jpg") : null; + Intent captureVideoIntent = showVideoIntent ? createCaptureIntent(MediaStore.ACTION_VIDEO_CAPTURE, "mp4") : null; + + if (getContentIntent == null && captureImageIntent == null && captureVideoIntent == null) { // cannot open anything: cancel file chooser sendBroadcast(new Intent(ACTION_FILE_CHOOSER_FINISHED)); finish(); } else { - Intent[] intentArray = - takePictureIntent != null ? new Intent[] {takePictureIntent} : new Intent[] {}; + ArrayList intentList = new ArrayList<>(); + + if (getContentIntent != null) { + intentList.add(getContentIntent); + } + + if (captureImageIntent != null) { + intentList.add(captureImageIntent); + } + if (captureVideoIntent != null) { + intentList.add(captureVideoIntent); + } Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); - chooserIntent.putExtra( - Intent.EXTRA_INTENT, galleryIntent != null ? galleryIntent : takePictureIntent); chooserIntent.putExtra(Intent.EXTRA_TITLE, getIntent().getStringExtra(EXTRA_TITLE)); - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); + + chooserIntent.putExtra(Intent.EXTRA_INTENT, intentList.get(0)); + intentList.remove(0); + if (intentList.size() > 0) { + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentList.toArray(new Intent[0])); + } startActivityForResult(chooserIntent, FILE_CHOOSER_REQUEST_CODE); } } - private Intent createGalleryIntent() { + private Intent createGetContentIntent() { Intent filesIntent = new Intent(Intent.ACTION_GET_CONTENT); - filesIntent.setType(getIntent().getStringExtra(EXTRA_TYPE)); + + if (getIntent().getBooleanExtra(EXTRA_ALLOW_MULTIPLE_FILES, false)) { + filesIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + } + + String[] acceptTypes = getIntent().getStringArrayExtra(EXTRA_ACCEPT_TYPES); + + if (acceptTypes.length == 0 || (acceptTypes.length == 1 && acceptTypes[0].length() == 0)) { + // empty array or only 1 empty string? -> accept all types + filesIntent.setType("*/*"); + } else if (acceptTypes.length == 1) { + filesIntent.setType(acceptTypes[0]); + } else { + // acceptTypes.length > 1 + filesIntent.setType("*/*"); + filesIntent.putExtra(Intent.EXTRA_MIME_TYPES, acceptTypes); + } + return (filesIntent.resolveActivity(getPackageManager()) != null) ? filesIntent : null; } - private Intent createCameraIntent() { - Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - if (takePictureIntent.resolveActivity(getPackageManager()) == null) { + private Intent createCaptureIntent(String type, String fileFormat) { + Intent captureIntent = new Intent(type); + if (captureIntent.resolveActivity(getPackageManager()) == null) { return null; } - // Create the File where the photo should go - cameraImageUri = getTempImageUri(); - takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri); - return takePictureIntent; + // Create the File where the output should go + Uri captureOutputUri = getTempUri(fileFormat); + potentialCaptureOutputUris.add(captureOutputUri); + + captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, captureOutputUri); + + return captureIntent; } private File getStorageDirectory() { @@ -89,11 +128,11 @@ private File getStorageDirectory() { return imageDirectory; } - private Uri getTempImageUri() { - String imageFileName = "IMG-" + simpleDateFormat.format(new Date()) + ".jpg"; - File imageFile = new File(getStorageDirectory(), imageFileName); + private Uri getTempUri(String format) { + String fileName = "CAPTURE-" + simpleDateFormat.format(new Date()) + "." + format; + File file = new File(getStorageDirectory(), fileName); return FileProvider.getUriForFile( - this, getApplicationContext().getPackageName() + ".generic.provider", imageFile); + this, getApplicationContext().getPackageName() + ".generic.provider", file); } private String getFileNameFromUri(Uri uri) { @@ -107,63 +146,65 @@ private String getFileNameFromUri(Uri uri) { } private Uri copyToLocalUri(Uri uri) { - InputStream in = null; - OutputStream out = null; - try { - File destination = new File(getStorageDirectory(), getFileNameFromUri(uri)); - in = getContentResolver().openInputStream(uri); - out = new FileOutputStream(destination); - - int cnt = 0; + File destination = new File(getStorageDirectory(), getFileNameFromUri(uri)); + + try (InputStream in = getContentResolver().openInputStream(uri); OutputStream out = new FileOutputStream(destination)) { byte[] buffer = new byte[1024]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); - cnt += len; } - - return FileProvider.getUriForFile( - this, getApplicationContext().getPackageName() + ".generic.provider", destination); - } catch (Exception e) { + return FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".generic.provider", destination); + } catch (IOException e) { Log.e("WEBVIEW", "Unable to copy selected image", e); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (out != null) { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } + e.printStackTrace(); + return null; } - - return null; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == FILE_CHOOSER_REQUEST_CODE) { - Intent intent = new Intent(ACTION_FILE_CHOOSER_FINISHED); + Intent fileChooserFinishedIntent = new Intent(ACTION_FILE_CHOOSER_FINISHED); if (resultCode == Activity.RESULT_OK) { - if (data != null && data.getDataString() != null) { - // result from file browser - final Uri uri = copyToLocalUri(data.getData()); - intent.putExtra(EXTRA_FILE_URI, uri.toString()); + if (data != null && (data.getDataString() != null || data.getClipData() != null)) { + if (data.getDataString() != null) { + // single result from file browser OR video from camera + Uri localUri = copyToLocalUri(data.getData()); + if (localUri != null) { + fileChooserFinishedIntent.putExtra(EXTRA_FILE_URIS, new String[]{localUri.toString()}); + } + } else if (data.getClipData() != null) { + // multiple results from file browser + int uriCount = data.getClipData().getItemCount(); + String[] uriStrings = new String[uriCount]; + + for (int i = 0; i < uriCount; i++) { + Uri localUri = copyToLocalUri(data.getClipData().getItemAt(i).getUri()); + if (localUri != null) { + uriStrings[i] = localUri.toString(); + } + } + fileChooserFinishedIntent.putExtra(EXTRA_FILE_URIS, uriStrings); + } } else { - // result from camera - intent.putExtra(EXTRA_FILE_URI, cameraImageUri.toString()); + // image result from camera (videos from the camera are handled above, but this if-branch could handle them too if this varies from device to device) + for (Uri captureOutputUri : potentialCaptureOutputUris) { + try { + // just opening an input stream (and closing immediately) to test if the Uri points to a valid file + // if it's not a real file, the below catch-clause gets executed and we continue with the next Uri in the loop. + getContentResolver().openInputStream(captureOutputUri).close(); + fileChooserFinishedIntent.putExtra(EXTRA_FILE_URIS, new String[]{captureOutputUri.toString()}); + // leave the loop, as only one of the potentialCaptureOutputUris is valid and we just found it + break; + } catch (IOException ignored) {} + } } } - sendBroadcast(intent); + sendBroadcast(fileChooserFinishedIntent); finish(); } else { super.onActivityResult(requestCode, resultCode, data); } } -} +} \ No newline at end of file diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 5e60fc661030..20cde24ef00f 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -6,10 +6,12 @@ import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URI; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_CAMERA_OPTION; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ALLOW_MULTIPLE_FILES; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URIS; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_IMAGE_OPTION; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_VIDEO_OPTION; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TYPE; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ACCEPT_TYPES; import android.Manifest; import android.content.BroadcastReceiver; @@ -21,34 +23,67 @@ import android.webkit.ValueCallback; import androidx.core.content.ContextCompat; +import java.util.Arrays; + public class FileChooserLauncher extends BroadcastReceiver { private Context context; private String title; - private String type; - private boolean showCameraOption; + private boolean allowMultipleFiles; + private boolean videoAcceptable; + private boolean imageAcceptable; private ValueCallback filePathCallback; + private String[] acceptTypes; public FileChooserLauncher( - Context context, - String title, - String type, - boolean showCameraOption, - ValueCallback filePathCallback) { + Context context, + boolean allowMultipleFiles, + ValueCallback filePathCallback, + String[] acceptTypes) { this.context = context; - this.title = title; - this.type = type; - this.showCameraOption = showCameraOption; + this.allowMultipleFiles = allowMultipleFiles; this.filePathCallback = filePathCallback; + this.acceptTypes = acceptTypes; + + if (acceptTypes.length == 0 || (acceptTypes.length == 1 && acceptTypes[0].length() == 0)) { + // acceptTypes empty -> accept anything + imageAcceptable = true; + videoAcceptable = true; + } + else { + for (String acceptType : acceptTypes) { + if (acceptType.startsWith("image/")) { + imageAcceptable = true; + } + else if (acceptType.startsWith("video/")) { + videoAcceptable = true; + } + } + } + + if (imageAcceptable && !videoAcceptable) { + title = context.getResources().getString(R.string.webview_image_chooser_title); + } + else if (videoAcceptable && !imageAcceptable) { + title = context.getResources().getString(R.string.webview_video_chooser_title); + } + else { + title = context.getResources().getString(R.string.webview_file_chooser_title); + } + } + + + private boolean canCameraProduceAcceptableType() { + return imageAcceptable || videoAcceptable; } private boolean hasCameraPermission() { return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) - == PackageManager.PERMISSION_GRANTED; + == PackageManager.PERMISSION_GRANTED; } public void start() { - if (!showCameraOption || hasCameraPermission()) { + if (!canCameraProduceAcceptableType() || hasCameraPermission()) { showFileChooser(); } else { IntentFilter intentFilter = new IntentFilter(); @@ -67,8 +102,10 @@ private void showFileChooser() { Intent intent = new Intent(context, FileChooserActivity.class); intent.putExtra(EXTRA_TITLE, title); - intent.putExtra(EXTRA_TYPE, type); - intent.putExtra(EXTRA_SHOW_CAMERA_OPTION, showCameraOption && hasCameraPermission()); + intent.putExtra(EXTRA_ACCEPT_TYPES, acceptTypes); + intent.putExtra(EXTRA_SHOW_IMAGE_OPTION, imageAcceptable && hasCameraPermission()); + intent.putExtra(EXTRA_SHOW_VIDEO_OPTION, videoAcceptable && hasCameraPermission()); + intent.putExtra(EXTRA_ALLOW_MULTIPLE_FILES, allowMultipleFiles); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } @@ -79,11 +116,21 @@ public void onReceive(Context context, Intent intent) { context.unregisterReceiver(this); showFileChooser(); } else if (intent.getAction().equals(ACTION_FILE_CHOOSER_FINISHED)) { - String uriString = intent.getStringExtra(EXTRA_FILE_URI); - Uri[] result = uriString != null ? new Uri[] {Uri.parse(uriString)} : null; + String[] uriStrings = intent.getStringArrayExtra(EXTRA_FILE_URIS); + Uri[] result = null; + + if (uriStrings != null) { + int uriStringCount = uriStrings.length; + result = new Uri[uriStringCount]; + + for (int i = 0; i < uriStringCount; i++) { + result[i] = Uri.parse(uriStrings[i]); + } + } + filePathCallback.onReceiveValue(result); context.unregisterReceiver(this); filePathCallback = null; } } -} +} \ No newline at end of file diff --git a/packages/webview_flutter/android/src/main/res/values/strings.xml b/packages/webview_flutter/android/src/main/res/values/strings.xml index 0a6a5f1b4fe1..a45adb97b5f7 100644 --- a/packages/webview_flutter/android/src/main/res/values/strings.xml +++ b/packages/webview_flutter/android/src/main/res/values/strings.xml @@ -1,5 +1,6 @@ Choose a file - image/* + Choose an image + Choose a video \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index b358e07b1c2d..93a744113d65 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -25,6 +25,8 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.platform.PlatformView; + +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -77,15 +79,17 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { return true; } - @Override + @Override public boolean onShowFileChooser( WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { + // info as of 2021-03-08: + // don't use fileChooserParams.getTitle() as it is (always? on Mi 9T Pro Android 10 at least) null + // don't use fileChooserParams.isCaptureEnabled() as it is (always? on Mi 9T Pro Android 10 at least) false, even when the file upload allows images or any file final Context context = webView.getContext(); - final String title = context.getResources().getString(R.string.webview_file_chooser_title); - final String type = context.getResources().getString(R.string.webview_file_chooser_type); - new FileChooserLauncher(context, title, type, true, filePathCallback).start(); + final boolean allowMultipleFiles = fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE; + new FileChooserLauncher(context, allowMultipleFiles, filePathCallback, fileChooserParams.getAcceptTypes()).start(); return true; } From edef9f70d3e340ca5458cbb29d4a4de45dc7721a Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:33:18 +0200 Subject: [PATCH 14/20] Fix formatting --- .../plugins/webviewflutter/Constants.java | 2 +- .../webviewflutter/FileChooserActivity.java | 31 ++++++++++++------- .../webviewflutter/FileChooserLauncher.java | 28 +++++++---------- .../webviewflutter/FlutterWebView.java | 11 ++++--- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java index cc884fc44a29..340c16099e53 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -17,4 +17,4 @@ public class Constants { static final String EXTRA_ALLOW_MULTIPLE_FILES = "extra_allow_multiple_files"; static final String WEBVIEW_STORAGE_DIRECTORY = "storage"; -} \ No newline at end of file +} diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index a1c82a926ca9..9030b51d1ccf 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -5,12 +5,12 @@ package io.flutter.plugins.webviewflutter; import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ACCEPT_TYPES; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ALLOW_MULTIPLE_FILES; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URIS; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_IMAGE_OPTION; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_VIDEO_OPTION; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ACCEPT_TYPES; import static io.flutter.plugins.webviewflutter.Constants.WEBVIEW_STORAGE_DIRECTORY; import android.app.Activity; @@ -43,13 +43,17 @@ public class FileChooserActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - showFileChooser(getIntent().getBooleanExtra(EXTRA_SHOW_IMAGE_OPTION, false), getIntent().getBooleanExtra(EXTRA_SHOW_VIDEO_OPTION, false)); + showFileChooser( + getIntent().getBooleanExtra(EXTRA_SHOW_IMAGE_OPTION, false), + getIntent().getBooleanExtra(EXTRA_SHOW_VIDEO_OPTION, false)); } private void showFileChooser(boolean showImageIntent, boolean showVideoIntent) { Intent getContentIntent = createGetContentIntent(); - Intent captureImageIntent = showImageIntent ? createCaptureIntent(MediaStore.ACTION_IMAGE_CAPTURE, "jpg") : null; - Intent captureVideoIntent = showVideoIntent ? createCaptureIntent(MediaStore.ACTION_VIDEO_CAPTURE, "mp4") : null; + Intent captureImageIntent = + showImageIntent ? createCaptureIntent(MediaStore.ACTION_IMAGE_CAPTURE, "jpg") : null; + Intent captureVideoIntent = + showVideoIntent ? createCaptureIntent(MediaStore.ACTION_VIDEO_CAPTURE, "mp4") : null; if (getContentIntent == null && captureImageIntent == null && captureVideoIntent == null) { // cannot open anything: cancel file chooser @@ -132,7 +136,7 @@ private Uri getTempUri(String format) { String fileName = "CAPTURE-" + simpleDateFormat.format(new Date()) + "." + format; File file = new File(getStorageDirectory(), fileName); return FileProvider.getUriForFile( - this, getApplicationContext().getPackageName() + ".generic.provider", file); + this, getApplicationContext().getPackageName() + ".generic.provider", file); } private String getFileNameFromUri(Uri uri) { @@ -148,13 +152,15 @@ private String getFileNameFromUri(Uri uri) { private Uri copyToLocalUri(Uri uri) { File destination = new File(getStorageDirectory(), getFileNameFromUri(uri)); - try (InputStream in = getContentResolver().openInputStream(uri); OutputStream out = new FileOutputStream(destination)) { + try (InputStream in = getContentResolver().openInputStream(uri); + OutputStream out = new FileOutputStream(destination)) { byte[] buffer = new byte[1024]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } - return FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".generic.provider", destination); + return FileProvider.getUriForFile( + this, getApplicationContext().getPackageName() + ".generic.provider", destination); } catch (IOException e) { Log.e("WEBVIEW", "Unable to copy selected image", e); e.printStackTrace(); @@ -172,7 +178,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { // single result from file browser OR video from camera Uri localUri = copyToLocalUri(data.getData()); if (localUri != null) { - fileChooserFinishedIntent.putExtra(EXTRA_FILE_URIS, new String[]{localUri.toString()}); + fileChooserFinishedIntent.putExtra( + EXTRA_FILE_URIS, new String[] {localUri.toString()}); } } else if (data.getClipData() != null) { // multiple results from file browser @@ -194,10 +201,12 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { // just opening an input stream (and closing immediately) to test if the Uri points to a valid file // if it's not a real file, the below catch-clause gets executed and we continue with the next Uri in the loop. getContentResolver().openInputStream(captureOutputUri).close(); - fileChooserFinishedIntent.putExtra(EXTRA_FILE_URIS, new String[]{captureOutputUri.toString()}); + fileChooserFinishedIntent.putExtra( + EXTRA_FILE_URIS, new String[] {captureOutputUri.toString()}); // leave the loop, as only one of the potentialCaptureOutputUris is valid and we just found it break; - } catch (IOException ignored) {} + } catch (IOException ignored) { + } } } } @@ -207,4 +216,4 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); } } -} \ No newline at end of file +} diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 20cde24ef00f..1efed252225a 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -6,12 +6,12 @@ import static io.flutter.plugins.webviewflutter.Constants.ACTION_FILE_CHOOSER_FINISHED; import static io.flutter.plugins.webviewflutter.Constants.ACTION_REQUEST_CAMERA_PERMISSION_FINISHED; +import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ACCEPT_TYPES; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ALLOW_MULTIPLE_FILES; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_FILE_URIS; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_IMAGE_OPTION; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_SHOW_VIDEO_OPTION; import static io.flutter.plugins.webviewflutter.Constants.EXTRA_TITLE; -import static io.flutter.plugins.webviewflutter.Constants.EXTRA_ACCEPT_TYPES; import android.Manifest; import android.content.BroadcastReceiver; @@ -23,7 +23,6 @@ import android.webkit.ValueCallback; import androidx.core.content.ContextCompat; -import java.util.Arrays; public class FileChooserLauncher extends BroadcastReceiver { @@ -36,10 +35,10 @@ public class FileChooserLauncher extends BroadcastReceiver { private String[] acceptTypes; public FileChooserLauncher( - Context context, - boolean allowMultipleFiles, - ValueCallback filePathCallback, - String[] acceptTypes) { + Context context, + boolean allowMultipleFiles, + ValueCallback filePathCallback, + String[] acceptTypes) { this.context = context; this.allowMultipleFiles = allowMultipleFiles; this.filePathCallback = filePathCallback; @@ -49,13 +48,11 @@ public FileChooserLauncher( // acceptTypes empty -> accept anything imageAcceptable = true; videoAcceptable = true; - } - else { + } else { for (String acceptType : acceptTypes) { if (acceptType.startsWith("image/")) { imageAcceptable = true; - } - else if (acceptType.startsWith("video/")) { + } else if (acceptType.startsWith("video/")) { videoAcceptable = true; } } @@ -63,23 +60,20 @@ else if (acceptType.startsWith("video/")) { if (imageAcceptable && !videoAcceptable) { title = context.getResources().getString(R.string.webview_image_chooser_title); - } - else if (videoAcceptable && !imageAcceptable) { + } else if (videoAcceptable && !imageAcceptable) { title = context.getResources().getString(R.string.webview_video_chooser_title); - } - else { + } else { title = context.getResources().getString(R.string.webview_file_chooser_title); } } - private boolean canCameraProduceAcceptableType() { return imageAcceptable || videoAcceptable; } private boolean hasCameraPermission() { return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) - == PackageManager.PERMISSION_GRANTED; + == PackageManager.PERMISSION_GRANTED; } public void start() { @@ -133,4 +127,4 @@ public void onReceive(Context context, Intent intent) { filePathCallback = null; } } -} \ No newline at end of file +} diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index 93a744113d65..c7f6eb1fb852 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -25,8 +25,6 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.platform.PlatformView; - -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -79,7 +77,7 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { return true; } - @Override + @Override public boolean onShowFileChooser( WebView webView, ValueCallback filePathCallback, @@ -88,8 +86,11 @@ public boolean onShowFileChooser( // don't use fileChooserParams.getTitle() as it is (always? on Mi 9T Pro Android 10 at least) null // don't use fileChooserParams.isCaptureEnabled() as it is (always? on Mi 9T Pro Android 10 at least) false, even when the file upload allows images or any file final Context context = webView.getContext(); - final boolean allowMultipleFiles = fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE; - new FileChooserLauncher(context, allowMultipleFiles, filePathCallback, fileChooserParams.getAcceptTypes()).start(); + final boolean allowMultipleFiles = + fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE; + new FileChooserLauncher( + context, allowMultipleFiles, filePathCallback, fileChooserParams.getAcceptTypes()) + .start(); return true; } From 9d60d07363139b2564c16f854e2ba8f5f433dce5 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:33:46 +0200 Subject: [PATCH 15/20] Remove unnecessary empty line --- .../io/flutter/plugins/webviewflutter/FileChooserLauncher.java | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 1efed252225a..626f6020fa13 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -23,7 +23,6 @@ import android.webkit.ValueCallback; import androidx.core.content.ContextCompat; - public class FileChooserLauncher extends BroadcastReceiver { private Context context; From 61a4315d07c5ee34ca5da8be49a975ca171b7298 Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:34:10 +0200 Subject: [PATCH 16/20] Fix license blocks --- .../main/java/io/flutter/plugins/webviewflutter/Constants.java | 2 +- .../io/flutter/plugins/webviewflutter/FileChooserActivity.java | 2 +- .../io/flutter/plugins/webviewflutter/FileChooserLauncher.java | 2 +- .../io/flutter/plugins/webviewflutter/GenericFileProvider.java | 2 +- .../plugins/webviewflutter/RequestCameraPermissionActivity.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java index 340c16099e53..0708c5700f3b 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java @@ -1,4 +1,4 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index 9030b51d1ccf..4bb2f4fcd972 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -1,4 +1,4 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java index 626f6020fa13..bdfb81702bcb 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java @@ -1,4 +1,4 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java index 3b9a218c7d7a..7f67bc8dd06b 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java @@ -1,4 +1,4 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java index de79f09dbde4..475155bfeeca 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java @@ -1,4 +1,4 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. From 60ed904a70254f1af7c7cbf33ae3bd43e7b443eb Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:47:53 +0200 Subject: [PATCH 17/20] Only support uploading of multiple files and different file types from Lollipop and up --- .../plugins/webviewflutter/FlutterWebView.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index c7f6eb1fb852..cd19e6bc8869 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -86,11 +86,12 @@ public boolean onShowFileChooser( // don't use fileChooserParams.getTitle() as it is (always? on Mi 9T Pro Android 10 at least) null // don't use fileChooserParams.isCaptureEnabled() as it is (always? on Mi 9T Pro Android 10 at least) false, even when the file upload allows images or any file final Context context = webView.getContext(); - final boolean allowMultipleFiles = - fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE; - new FileChooserLauncher( - context, allowMultipleFiles, filePathCallback, fileChooserParams.getAcceptTypes()) - .start(); + final boolean allowMultipleFiles = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + && fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE; + final String[] acceptTypes = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + ? fileChooserParams.getAcceptTypes() : new String[0]; + new FileChooserLauncher(context, allowMultipleFiles, filePathCallback, acceptTypes) + .start(); return true; } From 948eecbb20be18ba2c8e43ecf7ad096ac17da2ec Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 10:49:57 +0200 Subject: [PATCH 18/20] Use a private field for the log tag --- .../flutter/plugins/webviewflutter/FileChooserActivity.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java index 4bb2f4fcd972..737f0e6189c8 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java @@ -34,6 +34,7 @@ public class FileChooserActivity extends Activity { + private static final String TAG = "FileChooserActivity"; private static final int FILE_CHOOSER_REQUEST_CODE = 12322; private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); @@ -127,7 +128,7 @@ private Intent createCaptureIntent(String type, String fileFormat) { private File getStorageDirectory() { File imageDirectory = new File(getCacheDir(), WEBVIEW_STORAGE_DIRECTORY); if (!imageDirectory.exists() && !imageDirectory.mkdir()) { - Log.e("WEBVIEW", "Unable to create storage directory"); + Log.e(TAG, "Unable to create storage directory"); } return imageDirectory; } @@ -162,7 +163,7 @@ private Uri copyToLocalUri(Uri uri) { return FileProvider.getUriForFile( this, getApplicationContext().getPackageName() + ".generic.provider", destination); } catch (IOException e) { - Log.e("WEBVIEW", "Unable to copy selected image", e); + Log.e(TAG, "Unable to copy selected image", e); e.printStackTrace(); return null; } From 60aca8c58d9ee2690e923e405e00466bfb4e0a0a Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Fri, 18 Jun 2021 13:53:17 +0200 Subject: [PATCH 19/20] Fix formatting --- .../plugins/webviewflutter/FlutterWebView.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index cd19e6bc8869..5929203329b3 100644 --- a/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -86,12 +86,14 @@ public boolean onShowFileChooser( // don't use fileChooserParams.getTitle() as it is (always? on Mi 9T Pro Android 10 at least) null // don't use fileChooserParams.isCaptureEnabled() as it is (always? on Mi 9T Pro Android 10 at least) false, even when the file upload allows images or any file final Context context = webView.getContext(); - final boolean allowMultipleFiles = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + final boolean allowMultipleFiles = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE; - final String[] acceptTypes = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP - ? fileChooserParams.getAcceptTypes() : new String[0]; - new FileChooserLauncher(context, allowMultipleFiles, filePathCallback, acceptTypes) - .start(); + final String[] acceptTypes = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + ? fileChooserParams.getAcceptTypes() + : new String[0]; + new FileChooserLauncher(context, allowMultipleFiles, filePathCallback, acceptTypes).start(); return true; } From 4525fa5e9df45fe8a3c529ab01cfd05730b261db Mon Sep 17 00:00:00 2001 From: Ferry Spiering Date: Tue, 24 Aug 2021 08:27:04 +0200 Subject: [PATCH 20/20] Update to latest file structure --- .../main/java/io/flutter/plugins/webviewflutter/Constants.java | 0 .../io/flutter/plugins/webviewflutter/FileChooserActivity.java | 0 .../io/flutter/plugins/webviewflutter/FileChooserLauncher.java | 0 .../io/flutter/plugins/webviewflutter/GenericFileProvider.java | 0 .../plugins/webviewflutter/RequestCameraPermissionActivity.java | 0 .../{ => webview_flutter}/android/src/main/res/values/strings.xml | 0 .../android/src/main/res/xml/provider_paths.xml | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename packages/webview_flutter/{ => webview_flutter}/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java (100%) rename packages/webview_flutter/{ => webview_flutter}/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java (100%) rename packages/webview_flutter/{ => webview_flutter}/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java (100%) rename packages/webview_flutter/{ => webview_flutter}/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java (100%) rename packages/webview_flutter/{ => webview_flutter}/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java (100%) rename packages/webview_flutter/{ => webview_flutter}/android/src/main/res/values/strings.xml (100%) rename packages/webview_flutter/{ => webview_flutter}/android/src/main/res/xml/provider_paths.xml (100%) diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java similarity index 100% rename from packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java rename to packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/Constants.java diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java similarity index 100% rename from packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java rename to packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserActivity.java diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java similarity index 100% rename from packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java rename to packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserLauncher.java diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java similarity index 100% rename from packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java rename to packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/GenericFileProvider.java diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java b/packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java similarity index 100% rename from packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java rename to packages/webview_flutter/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/RequestCameraPermissionActivity.java diff --git a/packages/webview_flutter/android/src/main/res/values/strings.xml b/packages/webview_flutter/webview_flutter/android/src/main/res/values/strings.xml similarity index 100% rename from packages/webview_flutter/android/src/main/res/values/strings.xml rename to packages/webview_flutter/webview_flutter/android/src/main/res/values/strings.xml diff --git a/packages/webview_flutter/android/src/main/res/xml/provider_paths.xml b/packages/webview_flutter/webview_flutter/android/src/main/res/xml/provider_paths.xml similarity index 100% rename from packages/webview_flutter/android/src/main/res/xml/provider_paths.xml rename to packages/webview_flutter/webview_flutter/android/src/main/res/xml/provider_paths.xml