parent
428ecf7a31
commit
3731ef5f52
|
@ -3,7 +3,8 @@
|
||||||
- Updated Android context menu workaround
|
- Updated Android context menu workaround
|
||||||
- Calling `onCreateContextMenu` event on iOS also when the context menu is disabled in order to have the same effect as Android
|
- Calling `onCreateContextMenu` event on iOS also when the context menu is disabled in order to have the same effect as Android
|
||||||
- Added Android keyboard workaround to hide the keyboard when clicking other HTML elements, losing the focus on the previous input
|
- Added Android keyboard workaround to hide the keyboard when clicking other HTML elements, losing the focus on the previous input
|
||||||
- Added `onEnterFullscreen`, `onExitFullscreen` webview events
|
- Added `onEnterFullscreen`, `onExitFullscreen` webview events [#275](https://github.com/pichillilorenzo/flutter_inappwebview/issues/275)
|
||||||
|
- Added Android support to use camera on HTML inputs that requires it, such as `<input type="file" accept="image/*" capture>` [#353](https://github.com/pichillilorenzo/flutter_inappwebview/issues/353)
|
||||||
- Fixed `Print preview is not working? java.lang.IllegalStateException: Can print only from an activity` [#128](https://github.com/pichillilorenzo/flutter_inappwebview/issues/128)
|
- Fixed `Print preview is not working? java.lang.IllegalStateException: Can print only from an activity` [#128](https://github.com/pichillilorenzo/flutter_inappwebview/issues/128)
|
||||||
- Fixed `onJsAlert`, `onJsConfirm`, `onJsPrompt` for `InAppBrowser` on Android
|
- Fixed `onJsAlert`, `onJsConfirm`, `onJsPrompt` for `InAppBrowser` on Android
|
||||||
- Fixed `InAppBrowser.openWithSystemBrowser crash on iOS` [#358](https://github.com/pichillilorenzo/flutter_inappwebview/issues/358)
|
- Fixed `InAppBrowser.openWithSystemBrowser crash on iOS` [#358](https://github.com/pichillilorenzo/flutter_inappwebview/issues/358)
|
||||||
|
|
33
README.md
33
README.md
|
@ -121,6 +121,37 @@ Other useful `Info.plist` properties are:
|
||||||
* `NSAllowsLocalNetworking`: A Boolean value indicating whether to allow loading of local resources ([Official wiki](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowslocalnetworking));
|
* `NSAllowsLocalNetworking`: A Boolean value indicating whether to allow loading of local resources ([Official wiki](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowslocalnetworking));
|
||||||
* `NSAllowsArbitraryLoadsInWebContent`: A Boolean value indicating whether all App Transport Security restrictions are disabled for requests made from web views ([Official wiki](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowsarbitraryloadsinwebcontent)).
|
* `NSAllowsArbitraryLoadsInWebContent`: A Boolean value indicating whether all App Transport Security restrictions are disabled for requests made from web views ([Official wiki](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowsarbitraryloadsinwebcontent)).
|
||||||
|
|
||||||
|
### How to enable the usage of camera for HTML inputs such as `<input type="file" accept="image/*" capture>`
|
||||||
|
|
||||||
|
In order to be able to use camera, for example, for taking images through `<input type="file" accept="image/*" capture>` HTML tag, you need to ask camera permission.
|
||||||
|
To ask camera permission, you can simply use the [permission_handler](https://pub.dev/packages/permission_handler) plugin!
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```dart
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
Future main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await Permission.camera.request();
|
||||||
|
|
||||||
|
runApp(MyApp());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
On **Android**, you need to add some additional configurations.
|
||||||
|
Add the following codes inside the `<application>` tag of your `android/app/src/main/AndroidManifest.xml`:
|
||||||
|
```xml
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.flutter_inappwebview.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
```
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
For help getting started with Flutter, view our online
|
For help getting started with Flutter, view our online
|
||||||
|
@ -566,6 +597,8 @@ Event names that starts with `android` or `ios` are events platform-specific.
|
||||||
* `shouldInterceptFetchRequest`: Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API) (to use this event, the `useShouldInterceptFetchRequest` option must be `true`).
|
* `shouldInterceptFetchRequest`: Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API) (to use this event, the `useShouldInterceptFetchRequest` option must be `true`).
|
||||||
* `onPrint`: Event fired when `window.print()` is called from JavaScript side.
|
* `onPrint`: Event fired when `window.print()` is called from JavaScript side.
|
||||||
* `onLongPressHitTestResult`: Event fired when an HTML element of the webview has been clicked and held.
|
* `onLongPressHitTestResult`: Event fired when an HTML element of the webview has been clicked and held.
|
||||||
|
* `onEnterFullscreen`: Event fired when the current page has entered full screen mode.
|
||||||
|
* `onExitFullscreen`: Event fired when the current page has exited full screen mode.
|
||||||
* `androidOnSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android).
|
* `androidOnSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android).
|
||||||
* `androidOnPermissionRequest`: Event fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android).
|
* `androidOnPermissionRequest`: Event fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android).
|
||||||
* `androidOnGeolocationPermissionsShowPrompt`: Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin (available only on Android).
|
* `androidOnGeolocationPermissionsShowPrompt`: Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin (available only on Android).
|
||||||
|
|
|
@ -57,6 +57,10 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Bundle b = getIntent().getExtras();
|
Bundle b = getIntent().getExtras();
|
||||||
uuid = b.getString("uuid");
|
uuid = b.getString("uuid");
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,13 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
|
||||||
InAppWebViewOptions options = new InAppWebViewOptions();
|
InAppWebViewOptions options = new InAppWebViewOptions();
|
||||||
options.parse(initialOptions);
|
options.parse(initialOptions);
|
||||||
|
|
||||||
|
if (Shared.activity == null) {
|
||||||
|
Log.e(LOG_TAG, "\n\n\nERROR: Shared.activity is null!!!\n\n" +
|
||||||
|
"You need to upgrade your Flutter project to use the new Java Embedding API:\n\n" +
|
||||||
|
"- Take a look at the \"IMPORTANT Note for Android\" section here: https://github.com/pichillilorenzo/flutter_inappwebview#important-note-for-android\n" +
|
||||||
|
"- See the official wiki here: https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects\n\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
webView = new InAppWebView(Shared.activity, this, id, options, contextMenu, containerView);
|
webView = new InAppWebView(Shared.activity, this, id, options, contextMenu, containerView);
|
||||||
displayListenerProxy.onPostWebViewInitialization(displayManager);
|
displayListenerProxy.onPostWebViewInitialization(displayManager);
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
|
package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.Manifest;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Environment;
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -24,6 +25,7 @@ import android.webkit.ConsoleMessage;
|
||||||
import android.webkit.GeolocationPermissions;
|
import android.webkit.GeolocationPermissions;
|
||||||
import android.webkit.JsPromptResult;
|
import android.webkit.JsPromptResult;
|
||||||
import android.webkit.JsResult;
|
import android.webkit.JsResult;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
import android.webkit.PermissionRequest;
|
import android.webkit.PermissionRequest;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebChromeClient;
|
import android.webkit.WebChromeClient;
|
||||||
|
@ -33,13 +35,18 @@ import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
|
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.R;
|
import com.pichillilorenzo.flutter_inappwebview.R;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -49,7 +56,6 @@ import java.util.Map;
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
import io.flutter.plugin.common.PluginRegistry;
|
import io.flutter.plugin.common.PluginRegistry;
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_CANCELED;
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
|
||||||
public class InAppWebViewChromeClient extends WebChromeClient implements PluginRegistry.ActivityResultListener {
|
public class InAppWebViewChromeClient extends WebChromeClient implements PluginRegistry.ActivityResultListener {
|
||||||
|
@ -58,8 +64,31 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
private FlutterWebView flutterWebView;
|
private FlutterWebView flutterWebView;
|
||||||
private InAppBrowserActivity inAppBrowserActivity;
|
private InAppBrowserActivity inAppBrowserActivity;
|
||||||
public MethodChannel channel;
|
public MethodChannel channel;
|
||||||
private ValueCallback<Uri> mUploadMessage;
|
|
||||||
private final static int FILECHOOSER_RESULTCODE = 1;
|
private static final String fileProviderAuthorityExtension = "flutter_inappwebview.fileprovider";
|
||||||
|
|
||||||
|
private static final int PICKER = 1;
|
||||||
|
private static final int PICKER_LEGACY = 3;
|
||||||
|
final String DEFAULT_MIME_TYPES = "*/*";
|
||||||
|
private Uri outputFileUri;
|
||||||
|
|
||||||
|
protected static final FrameLayout.LayoutParams FULLSCREEN_LAYOUT_PARAMS = new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER);
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||||
|
protected static final int FULLSCREEN_SYSTEM_UI_VISIBILITY_KITKAT = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||||
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||||
|
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||||
|
View.SYSTEM_UI_FLAG_IMMERSIVE |
|
||||||
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||||
|
|
||||||
|
protected static final int FULLSCREEN_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||||
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||||
|
View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||||
|
|
||||||
private View mCustomView;
|
private View mCustomView;
|
||||||
private WebChromeClient.CustomViewCallback mCustomViewCallback;
|
private WebChromeClient.CustomViewCallback mCustomViewCallback;
|
||||||
|
@ -91,13 +120,15 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
@Override
|
@Override
|
||||||
public void onHideCustomView() {
|
public void onHideCustomView() {
|
||||||
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
||||||
View decorView = activity.getWindow().getDecorView();
|
|
||||||
|
View decorView = getRootView();
|
||||||
((FrameLayout) decorView).removeView(this.mCustomView);
|
((FrameLayout) decorView).removeView(this.mCustomView);
|
||||||
this.mCustomView = null;
|
this.mCustomView = null;
|
||||||
decorView.setSystemUiVisibility(this.mOriginalSystemUiVisibility);
|
decorView.setSystemUiVisibility(this.mOriginalSystemUiVisibility);
|
||||||
activity.setRequestedOrientation(this.mOriginalOrientation);
|
activity.setRequestedOrientation(this.mOriginalOrientation);
|
||||||
this.mCustomViewCallback.onCustomViewHidden();
|
this.mCustomViewCallback.onCustomViewHidden();
|
||||||
this.mCustomViewCallback = null;
|
this.mCustomViewCallback = null;
|
||||||
|
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||||
|
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
if (inAppBrowserActivity != null)
|
if (inAppBrowserActivity != null)
|
||||||
|
@ -113,35 +144,21 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
}
|
}
|
||||||
|
|
||||||
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
||||||
final View decorView = activity.getWindow().getDecorView();
|
|
||||||
|
View decorView = getRootView();
|
||||||
this.mCustomView = paramView;
|
this.mCustomView = paramView;
|
||||||
this.mOriginalSystemUiVisibility = decorView.getSystemUiVisibility();
|
this.mOriginalSystemUiVisibility = decorView.getSystemUiVisibility();
|
||||||
this.mOriginalOrientation = activity.getRequestedOrientation();
|
this.mOriginalOrientation = activity.getRequestedOrientation();
|
||||||
this.mCustomViewCallback = paramCustomViewCallback;
|
this.mCustomViewCallback = paramCustomViewCallback;
|
||||||
this.mCustomView.setBackgroundColor(Color.parseColor("#000000"));
|
this.mCustomView.setBackgroundColor(Color.BLACK);
|
||||||
((FrameLayout) decorView).addView(this.mCustomView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
decorView.setSystemUiVisibility(
|
decorView.setSystemUiVisibility(FULLSCREEN_SYSTEM_UI_VISIBILITY_KITKAT);
|
||||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
|
||||||
// Set the content to appear under the system bars so that the
|
|
||||||
// content doesn't resize when the system bars hide and show.
|
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
||||||
// Hide the nav bar and status bar
|
|
||||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN);
|
|
||||||
} else {
|
} else {
|
||||||
decorView.setSystemUiVisibility(
|
decorView.setSystemUiVisibility(FULLSCREEN_SYSTEM_UI_VISIBILITY);
|
||||||
// Set the content to appear under the system bars so that the
|
|
||||||
// content doesn't resize when the system bars hide and show.
|
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
||||||
// Hide the nav bar and status bar
|
|
||||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN);
|
|
||||||
}
|
}
|
||||||
|
activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||||
|
((FrameLayout) decorView).addView(this.mCustomView, FULLSCREEN_LAYOUT_PARAMS);
|
||||||
|
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
if (inAppBrowserActivity != null)
|
if (inAppBrowserActivity != null)
|
||||||
|
@ -577,70 +594,323 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
super.onReceivedIcon(view, icon);
|
super.onReceivedIcon(view, icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Android 3.0+
|
protected ViewGroup getRootView() {
|
||||||
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
|
|
||||||
mUploadMessage = uploadMsg;
|
|
||||||
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
|
||||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
|
||||||
i.setType("image/*");
|
|
||||||
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
||||||
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
|
return (ViewGroup) activity.findViewById(android.R.id.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Android 3.0+
|
protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType) {
|
||||||
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
|
startPhotoPickerIntent(filePathCallback, acceptType);
|
||||||
mUploadMessage = uploadMsg;
|
|
||||||
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
|
||||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
|
||||||
i.setType("*/*");
|
|
||||||
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
|
||||||
activity.startActivityForResult(
|
|
||||||
Intent.createChooser(i, "File Browser"),
|
|
||||||
FILECHOOSER_RESULTCODE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Android 4.1
|
protected void openFileChooser(ValueCallback<Uri> filePathCallback) {
|
||||||
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
|
startPhotoPickerIntent(filePathCallback, "");
|
||||||
mUploadMessage = uploadMsg;
|
|
||||||
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
|
||||||
i.addCategory(Intent.CATEGORY_OPENABLE);
|
|
||||||
i.setType("image/*");
|
|
||||||
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
|
||||||
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Android 5.0+
|
protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
|
||||||
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
|
startPhotoPickerIntent(filePathCallback, acceptType);
|
||||||
InAppWebViewFlutterPlugin.uploadMessageArray = filePathCallback;
|
|
||||||
try {
|
|
||||||
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
||||||
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
||||||
contentSelectionIntent.setType("*/*");
|
|
||||||
Intent[] intentArray;
|
|
||||||
intentArray = new Intent[0];
|
|
||||||
|
|
||||||
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
|
|
||||||
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
|
|
||||||
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
|
|
||||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
|
|
||||||
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
|
||||||
activity.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
@Override
|
||||||
|
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
|
||||||
|
String[] acceptTypes = fileChooserParams.getAcceptTypes();
|
||||||
|
boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
|
||||||
|
Intent intent = fileChooserParams.createIntent();
|
||||||
|
return startPhotoPickerIntent(filePathCallback, intent, acceptTypes, allowMultiple);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (requestCode == FILECHOOSER_RESULTCODE && (resultCode == RESULT_OK || resultCode == RESULT_CANCELED)) {
|
if (InAppWebViewFlutterPlugin.filePathCallback == null && InAppWebViewFlutterPlugin.filePathCallbackLegacy == null) {
|
||||||
InAppWebViewFlutterPlugin.uploadMessageArray.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// based off of which button was pressed, we get an activity result and a file
|
||||||
|
// the camera activity doesn't properly return the filename* (I think?) so we use
|
||||||
|
// this filename instead
|
||||||
|
switch (requestCode) {
|
||||||
|
case PICKER:
|
||||||
|
if (resultCode != RESULT_OK) {
|
||||||
|
if (InAppWebViewFlutterPlugin.filePathCallback != null) {
|
||||||
|
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Uri result[] = this.getSelectedFiles(data, resultCode);
|
||||||
|
if (result != null) {
|
||||||
|
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(result);
|
||||||
|
} else {
|
||||||
|
InAppWebViewFlutterPlugin.filePathCallback.onReceiveValue(new Uri[]{outputFileUri});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PICKER_LEGACY:
|
||||||
|
Uri result = resultCode != Activity.RESULT_OK ? null : data == null ? outputFileUri : data.getData();
|
||||||
|
InAppWebViewFlutterPlugin.filePathCallbackLegacy.onReceiveValue(result);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
InAppWebViewFlutterPlugin.filePathCallback = null;
|
||||||
|
InAppWebViewFlutterPlugin.filePathCallbackLegacy = null;
|
||||||
|
outputFileUri = null;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Uri[] getSelectedFiles(Intent data, int resultCode) {
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have one file selected
|
||||||
|
if (data.getData() != null) {
|
||||||
|
if (resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
return WebChromeClient.FileChooserParams.parseResult(resultCode, data);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have multiple files selected
|
||||||
|
if (data.getClipData() != null) {
|
||||||
|
final int numSelectedFiles = data.getClipData().getItemCount();
|
||||||
|
Uri[] result = new Uri[numSelectedFiles];
|
||||||
|
for (int i = 0; i < numSelectedFiles; i++) {
|
||||||
|
result[i] = data.getClipData().getItemAt(i).getUri();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startPhotoPickerIntent(ValueCallback<Uri> filePathCallback, String acceptType) {
|
||||||
|
InAppWebViewFlutterPlugin.filePathCallbackLegacy = filePathCallback;
|
||||||
|
|
||||||
|
Intent fileChooserIntent = getFileChooserIntent(acceptType);
|
||||||
|
Intent chooserIntent = Intent.createChooser(fileChooserIntent, "");
|
||||||
|
|
||||||
|
ArrayList<Parcelable> extraIntents = new ArrayList<>();
|
||||||
|
if (acceptsImages(acceptType)) {
|
||||||
|
extraIntents.add(getPhotoIntent());
|
||||||
|
}
|
||||||
|
if (acceptsVideo(acceptType)) {
|
||||||
|
extraIntents.add(getVideoIntent());
|
||||||
|
}
|
||||||
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
|
||||||
|
|
||||||
|
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
||||||
|
if (chooserIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||||
|
activity.startActivityForResult(chooserIntent, PICKER_LEGACY);
|
||||||
|
} else {
|
||||||
|
Log.d(LOG_TAG, "there is no Activity to handle this Intent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
public boolean startPhotoPickerIntent(final ValueCallback<Uri[]> callback, final Intent intent, final String[] acceptTypes, final boolean allowMultiple) {
|
||||||
|
InAppWebViewFlutterPlugin.filePathCallback = callback;
|
||||||
|
|
||||||
|
ArrayList<Parcelable> extraIntents = new ArrayList<>();
|
||||||
|
if (!needsCameraPermission()) {
|
||||||
|
if (acceptsImages(acceptTypes)) {
|
||||||
|
extraIntents.add(getPhotoIntent());
|
||||||
|
}
|
||||||
|
if (acceptsVideo(acceptTypes)) {
|
||||||
|
extraIntents.add(getVideoIntent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent fileSelectionIntent = getFileChooserIntent(acceptTypes, allowMultiple);
|
||||||
|
|
||||||
|
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
|
||||||
|
chooserIntent.putExtra(Intent.EXTRA_INTENT, fileSelectionIntent);
|
||||||
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
|
||||||
|
|
||||||
|
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
||||||
|
if (chooserIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||||
|
activity.startActivityForResult(chooserIntent, PICKER);
|
||||||
|
} else {
|
||||||
|
Log.d(LOG_TAG, "there is no Activity to handle this Intent");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean needsCameraPermission() {
|
||||||
|
boolean needed = false;
|
||||||
|
|
||||||
|
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
||||||
|
PackageManager packageManager = activity.getPackageManager();
|
||||||
|
try {
|
||||||
|
String[] requestedPermissions = packageManager.getPackageInfo(activity.getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS).requestedPermissions;
|
||||||
|
if (Arrays.asList(requestedPermissions).contains(Manifest.permission.CAMERA)
|
||||||
|
&& ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
needed = true;
|
||||||
|
}
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent getPhotoIntent() {
|
||||||
|
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
|
outputFileUri = getOutputUri(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
|
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent getVideoIntent() {
|
||||||
|
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
|
||||||
|
outputFileUri = getOutputUri(MediaStore.ACTION_VIDEO_CAPTURE);
|
||||||
|
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent getFileChooserIntent(String acceptTypes) {
|
||||||
|
String _acceptTypes = acceptTypes;
|
||||||
|
if (acceptTypes.isEmpty()) {
|
||||||
|
_acceptTypes = DEFAULT_MIME_TYPES;
|
||||||
|
}
|
||||||
|
if (acceptTypes.matches("\\.\\w+")) {
|
||||||
|
_acceptTypes = getMimeTypeFromExtension(acceptTypes.replace(".", ""));
|
||||||
|
}
|
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
intent.setType(_acceptTypes);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||||
|
private Intent getFileChooserIntent(String[] acceptTypes, boolean allowMultiple) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
intent.setType("*/*");
|
||||||
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, getAcceptedMimeType(acceptTypes));
|
||||||
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean acceptsImages(String types) {
|
||||||
|
String mimeType = types;
|
||||||
|
if (types.matches("\\.\\w+")) {
|
||||||
|
mimeType = getMimeTypeFromExtension(types.replace(".", ""));
|
||||||
|
}
|
||||||
|
return mimeType.isEmpty() || mimeType.toLowerCase().contains("image");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean acceptsImages(String[] types) {
|
||||||
|
String[] mimeTypes = getAcceptedMimeType(types);
|
||||||
|
return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "image");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean acceptsVideo(String types) {
|
||||||
|
String mimeType = types;
|
||||||
|
if (types.matches("\\.\\w+")) {
|
||||||
|
mimeType = getMimeTypeFromExtension(types.replace(".", ""));
|
||||||
|
}
|
||||||
|
return mimeType.isEmpty() || mimeType.toLowerCase().contains("video");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean acceptsVideo(String[] types) {
|
||||||
|
String[] mimeTypes = getAcceptedMimeType(types);
|
||||||
|
return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "video");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean arrayContainsString(String[] array, String pattern) {
|
||||||
|
for (String content : array) {
|
||||||
|
if (content.contains(pattern)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getAcceptedMimeType(String[] types) {
|
||||||
|
if (isArrayEmpty(types)) {
|
||||||
|
return new String[]{DEFAULT_MIME_TYPES};
|
||||||
|
}
|
||||||
|
String[] mimeTypes = new String[types.length];
|
||||||
|
for (int i = 0; i < types.length; i++) {
|
||||||
|
String t = types[i];
|
||||||
|
// convert file extensions to mime types
|
||||||
|
if (t.matches("\\.\\w+")) {
|
||||||
|
String mimeType = getMimeTypeFromExtension(t.replace(".", ""));
|
||||||
|
mimeTypes[i] = mimeType;
|
||||||
|
} else {
|
||||||
|
mimeTypes[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMimeTypeFromExtension(String extension) {
|
||||||
|
String type = null;
|
||||||
|
if (extension != null) {
|
||||||
|
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uri getOutputUri(String intentType) {
|
||||||
|
File capturedFile = null;
|
||||||
|
try {
|
||||||
|
capturedFile = getCapturedFile(intentType);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(LOG_TAG, "Error occurred while creating the File", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// for versions below 6.0 (23) we use the old File creation & permissions model
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return Uri.fromFile(capturedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
||||||
|
// for versions 6.0+ (23) we use the FileProvider to avoid runtime permissions
|
||||||
|
String packageName = activity.getApplicationContext().getPackageName();
|
||||||
|
return FileProvider.getUriForFile(activity.getApplicationContext(), packageName + "." + fileProviderAuthorityExtension, capturedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getCapturedFile(String intentType) throws IOException {
|
||||||
|
String prefix = "";
|
||||||
|
String suffix = "";
|
||||||
|
String dir = "";
|
||||||
|
String filename = "";
|
||||||
|
|
||||||
|
if (intentType.equals(MediaStore.ACTION_IMAGE_CAPTURE)) {
|
||||||
|
prefix = "image-";
|
||||||
|
suffix = ".jpg";
|
||||||
|
dir = Environment.DIRECTORY_PICTURES;
|
||||||
|
} else if (intentType.equals(MediaStore.ACTION_VIDEO_CAPTURE)) {
|
||||||
|
prefix = "video-";
|
||||||
|
suffix = ".mp4";
|
||||||
|
dir = Environment.DIRECTORY_MOVIES;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = prefix + String.valueOf(System.currentTimeMillis()) + suffix;
|
||||||
|
|
||||||
|
// for versions below 6.0 (23) we use the old File creation & permissions model
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
// only this Directory works on all tested Android versions
|
||||||
|
// ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
|
||||||
|
File storageDir = Environment.getExternalStoragePublicDirectory(dir);
|
||||||
|
return new File(storageDir, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity activity = inAppBrowserActivity != null ? inAppBrowserActivity : Shared.activity;
|
||||||
|
File storageDir = activity.getApplicationContext().getExternalFilesDir(null);
|
||||||
|
return File.createTempFile(filename, suffix, storageDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean isArrayEmpty(String[] arr) {
|
||||||
|
// when our array returned from getAcceptTypes() has no values set from the webview
|
||||||
|
// i.e. <input type="file" />, without any "accept" attr
|
||||||
|
// will be an array with one empty string element, afaik
|
||||||
|
return arr.length == 0 || (arr.length == 1 && arr[0].length() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPermissionRequest(final PermissionRequest request) {
|
public void onPermissionRequest(final PermissionRequest request) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
|
|
@ -28,7 +28,8 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
||||||
public static MyCookieManager myCookieManager;
|
public static MyCookieManager myCookieManager;
|
||||||
public static CredentialDatabaseHandler credentialDatabaseHandler;
|
public static CredentialDatabaseHandler credentialDatabaseHandler;
|
||||||
public static MyWebStorage myWebStorage;
|
public static MyWebStorage myWebStorage;
|
||||||
public static ValueCallback<Uri[]> uploadMessageArray;
|
public static ValueCallback<Uri> filePathCallbackLegacy;
|
||||||
|
public static ValueCallback<Uri[]> filePathCallback;
|
||||||
|
|
||||||
public InAppWebViewFlutterPlugin() {}
|
public InAppWebViewFlutterPlugin() {}
|
||||||
|
|
||||||
|
@ -95,7 +96,8 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
|
||||||
inAppWebViewStatic.dispose();
|
inAppWebViewStatic.dispose();
|
||||||
inAppWebViewStatic = null;
|
inAppWebViewStatic = null;
|
||||||
}
|
}
|
||||||
uploadMessageArray = null;
|
filePathCallbackLegacy = null;
|
||||||
|
filePathCallback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<external-path name="external_files" path="."/>
|
||||||
|
</paths>
|
|
@ -1 +1 @@
|
||||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]}],"date_created":"2020-05-23 12:13:45.779062","version":"1.17.1"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-23 18:32:59.790460","version":"1.17.1"}
|
|
@ -65,6 +65,16 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.flutter_inappwebview.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
<!-- <provider-->
|
<!-- <provider-->
|
||||||
<!-- android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"-->
|
<!-- android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"-->
|
||||||
<!-- android:authorities="${applicationId}.flutter_downloader.provider"-->
|
<!-- android:authorities="${applicationId}.flutter_downloader.provider"-->
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
package com.pichillilorenzo.flutterwebviewexample;
|
package com.pichillilorenzo.flutterwebviewexample;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity;
|
import io.flutter.embedding.android.FlutterActivity;
|
||||||
import io.flutter.embedding.engine.FlutterEngine;
|
|
||||||
|
|
||||||
public class MainActivity extends FlutterActivity {
|
public class MainActivity extends FlutterActivity {
|
||||||
@Override
|
|
||||||
public void configureFlutterEngine(FlutterEngine flutterEngine) {
|
|
||||||
flutterEngine.getPlugins().add(new InAppWebViewFlutterPlugin());
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -6,13 +6,15 @@ import 'package:flutter_inappwebview_example/chrome_safari_browser_example.scree
|
||||||
import 'package:flutter_inappwebview_example/headless_in_app_webview.screen.dart';
|
import 'package:flutter_inappwebview_example/headless_in_app_webview.screen.dart';
|
||||||
import 'package:flutter_inappwebview_example/in_app_webiew_example.screen.dart';
|
import 'package:flutter_inappwebview_example/in_app_webiew_example.screen.dart';
|
||||||
import 'package:flutter_inappwebview_example/in_app_browser_example.screen.dart';
|
import 'package:flutter_inappwebview_example/in_app_browser_example.screen.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
// InAppLocalhostServer localhostServer = new InAppLocalhostServer();
|
// InAppLocalhostServer localhostServer = new InAppLocalhostServer();
|
||||||
|
|
||||||
Future main() async {
|
Future main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
// await Permission.camera.request();
|
||||||
// await localhostServer.start();
|
// await localhostServer.start();
|
||||||
runApp(new MyApp());
|
runApp(MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
Drawer myDrawer({@required BuildContext context}) {
|
Drawer myDrawer({@required BuildContext context}) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ dependencies:
|
||||||
cupertino_icons: ^0.1.2
|
cupertino_icons: ^0.1.2
|
||||||
# flutter_downloader: ^1.3.2
|
# flutter_downloader: ^1.3.2
|
||||||
# path_provider: ^1.4.0
|
# path_provider: ^1.4.0
|
||||||
# permission_handler: ^3.3.0
|
permission_handler: ^5.0.0+hotfix.6
|
||||||
# connectivity: ^0.4.5+6
|
# connectivity: ^0.4.5+6
|
||||||
flutter_inappwebview:
|
flutter_inappwebview:
|
||||||
path: ../
|
path: ../
|
||||||
|
|
|
@ -45,6 +45,8 @@ class HeadlessInAppWebView implements WebView {
|
||||||
this.onUpdateVisitedHistory,
|
this.onUpdateVisitedHistory,
|
||||||
this.onPrint,
|
this.onPrint,
|
||||||
this.onLongPressHitTestResult,
|
this.onLongPressHitTestResult,
|
||||||
|
this.onEnterFullscreen,
|
||||||
|
this.onExitFullscreen,
|
||||||
this.androidOnSafeBrowsingHit,
|
this.androidOnSafeBrowsingHit,
|
||||||
this.androidOnPermissionRequest,
|
this.androidOnPermissionRequest,
|
||||||
this.androidOnGeolocationPermissionsShowPrompt,
|
this.androidOnGeolocationPermissionsShowPrompt,
|
||||||
|
@ -268,4 +270,10 @@ class HeadlessInAppWebView implements WebView {
|
||||||
InAppWebViewController controller,
|
InAppWebViewController controller,
|
||||||
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)
|
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)
|
||||||
shouldOverrideUrlLoading;
|
shouldOverrideUrlLoading;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final void Function(InAppWebViewController controller) onEnterFullscreen;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final void Function(InAppWebViewController controller) onExitFullscreen;
|
||||||
}
|
}
|
|
@ -440,6 +440,12 @@ class InAppBrowser {
|
||||||
///[hitTestResult] represents the hit result for hitting an HTML elements.
|
///[hitTestResult] represents the hit result for hitting an HTML elements.
|
||||||
void onLongPressHitTestResult(InAppWebViewHitTestResult hitTestResult) {}
|
void onLongPressHitTestResult(InAppWebViewHitTestResult hitTestResult) {}
|
||||||
|
|
||||||
|
///Event fired when the current page has entered full screen mode.
|
||||||
|
void onEnterFullscreen() {}
|
||||||
|
|
||||||
|
///Event fired when the current page has exited full screen mode.
|
||||||
|
void onExitFullscreen() {}
|
||||||
|
|
||||||
///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing.
|
///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing.
|
||||||
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
|
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
|
||||||
///
|
///
|
||||||
|
|
|
@ -68,6 +68,8 @@ class InAppWebView extends StatefulWidget implements WebView {
|
||||||
this.onUpdateVisitedHistory,
|
this.onUpdateVisitedHistory,
|
||||||
this.onPrint,
|
this.onPrint,
|
||||||
this.onLongPressHitTestResult,
|
this.onLongPressHitTestResult,
|
||||||
|
this.onEnterFullscreen,
|
||||||
|
this.onExitFullscreen,
|
||||||
this.androidOnSafeBrowsingHit,
|
this.androidOnSafeBrowsingHit,
|
||||||
this.androidOnPermissionRequest,
|
this.androidOnPermissionRequest,
|
||||||
this.androidOnGeolocationPermissionsShowPrompt,
|
this.androidOnGeolocationPermissionsShowPrompt,
|
||||||
|
@ -246,6 +248,12 @@ class InAppWebView extends StatefulWidget implements WebView {
|
||||||
InAppWebViewController controller,
|
InAppWebViewController controller,
|
||||||
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)
|
ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest)
|
||||||
shouldOverrideUrlLoading;
|
shouldOverrideUrlLoading;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final void Function(InAppWebViewController controller) onEnterFullscreen;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final void Function(InAppWebViewController controller) onExitFullscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InAppWebViewState extends State<InAppWebView> {
|
class _InAppWebViewState extends State<InAppWebView> {
|
||||||
|
|
|
@ -433,6 +433,20 @@ class InAppWebViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "onEnterFullscreen":
|
||||||
|
if (_webview != null &&
|
||||||
|
_webview.onEnterFullscreen != null)
|
||||||
|
_webview.onEnterFullscreen(this);
|
||||||
|
else if (_inAppBrowser != null)
|
||||||
|
_inAppBrowser.onEnterFullscreen();
|
||||||
|
break;
|
||||||
|
case "onExitFullscreen":
|
||||||
|
if (_webview != null &&
|
||||||
|
_webview.onExitFullscreen != null)
|
||||||
|
_webview.onExitFullscreen(this);
|
||||||
|
else if (_inAppBrowser != null)
|
||||||
|
_inAppBrowser.onExitFullscreen();
|
||||||
|
break;
|
||||||
case "onCallJsHandler":
|
case "onCallJsHandler":
|
||||||
String handlerName = call.arguments["handlerName"];
|
String handlerName = call.arguments["handlerName"];
|
||||||
// decode args to json
|
// decode args to json
|
||||||
|
|
|
@ -237,6 +237,12 @@ abstract class WebView {
|
||||||
final void Function(InAppWebViewController controller,
|
final void Function(InAppWebViewController controller,
|
||||||
InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult;
|
InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult;
|
||||||
|
|
||||||
|
///Event fired when the current page has entered full screen mode.
|
||||||
|
final void Function(InAppWebViewController controller) onEnterFullscreen;
|
||||||
|
|
||||||
|
///Event fired when the current page has exited full screen mode.
|
||||||
|
final void Function(InAppWebViewController controller) onExitFullscreen;
|
||||||
|
|
||||||
///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing.
|
///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing.
|
||||||
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
|
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
|
||||||
///
|
///
|
||||||
|
@ -341,6 +347,8 @@ abstract class WebView {
|
||||||
this.onUpdateVisitedHistory,
|
this.onUpdateVisitedHistory,
|
||||||
this.onPrint,
|
this.onPrint,
|
||||||
this.onLongPressHitTestResult,
|
this.onLongPressHitTestResult,
|
||||||
|
this.onEnterFullscreen,
|
||||||
|
this.onExitFullscreen,
|
||||||
this.androidOnSafeBrowsingHit,
|
this.androidOnSafeBrowsingHit,
|
||||||
this.androidOnPermissionRequest,
|
this.androidOnPermissionRequest,
|
||||||
this.androidOnGeolocationPermissionsShowPrompt,
|
this.androidOnGeolocationPermissionsShowPrompt,
|
||||||
|
|
Loading…
Reference in New Issue