Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[webview_flutter] Add file upload on Android #5172

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/webview_flutter/webview_flutter/lib/src/webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ typedef PageFinishedCallback = void Function(String url);
/// Signature for when a [WebView] is loading a page.
typedef PageLoadingCallback = void Function(int progress);

typedef ShowFileChooserCallBack = Future<List<String>> Function();

/// Signature for when a [WebView] has failed to load a resource.
typedef WebResourceErrorCallback = void Function(WebResourceError error);

Expand Down Expand Up @@ -90,6 +92,7 @@ class WebView extends StatefulWidget {
this.onPageStarted,
this.onPageFinished,
this.onProgress,
this.onShowFileChooser,
this.onWebResourceError,
this.debuggingEnabled = false,
this.gestureNavigationEnabled = false,
Expand Down Expand Up @@ -238,6 +241,9 @@ class WebView extends StatefulWidget {
/// Invoked when a page is loading.
final PageLoadingCallback? onProgress;

/// Invoked when a page is loading.
final ShowFileChooserCallBack? onShowFileChooser;

/// Invoked when a web resource has failed to load.
///
/// This callback is only called for the main page.
Expand Down Expand Up @@ -484,6 +490,14 @@ class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler {
}
}

@override
Future<List<String>> onShowFileChooser() async {
if (_widget.onShowFileChooser != null) {
return await _widget.onShowFileChooser!();
}
return [];
}

@override
void onWebResourceError(WebResourceError error) {
if (_widget.onWebResourceError != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// 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.

// Autogenerated from Pigeon (v1.0.9), do not edit directly.
// See also: https://pub.dev/packages/pigeon

Expand Down Expand Up @@ -2197,6 +2193,22 @@ public void onProgressChanged(
callback.reply(null);
});
}

public void onShowFileChooser(
Long instanceIdArg, Long webViewInstanceIdArg, Reply<List<String>> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser",
getCodec());
channel.send(
new ArrayList<Object>(Arrays.asList(instanceIdArg, webViewInstanceIdArg)),
channelReply -> {
@SuppressWarnings("ConstantConditions")
List<String> output = (List<String>) channelReply;
callback.reply(output);
});
}
}

private static class WebStorageHostApiCodec extends StandardMessageCodec {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.webkit.WebView;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientFlutterApi;
import java.util.List;

/**
* Flutter Api implementation for {@link WebChromeClient}.
Expand Down Expand Up @@ -39,6 +40,15 @@ public void onProgressChanged(
callback);
}

/** Passes arguments from {@link WebChromeClient#onProgressChanged} to Dart. */
public void onShowFileChooser(
WebChromeClient webChromeClient, WebView webView, Reply<List<String>> callback) {
super.onShowFileChooser(
instanceManager.getInstanceId(webChromeClient),
instanceManager.getInstanceId(webView),
callback);
}

/**
* Communicates to Dart that the reference to a {@link WebChromeClient}} was removed.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

package io.flutter.plugins.webviewflutter;

import android.net.Uri;
import android.os.Build;
import android.os.Message;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
Expand All @@ -14,7 +16,10 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientFlutterApi;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi;
import java.io.File;
import java.util.List;

/**
* Host api implementation for {@link WebChromeClient}.
Expand Down Expand Up @@ -100,6 +105,30 @@ public boolean shouldOverrideUrlLoading(WebView windowWebView, String url) {
return true;
}

@Override
public boolean onShowFileChooser(
WebView view,
ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
if (flutterApi != null) {
flutterApi.onShowFileChooser(
this,
view,
new WebChromeClientFlutterApi.Reply<List<String>>() {
public void reply(List<String> paths) {
final Uri[] uris = new Uri[paths.size()];
for (int i = 0; i < uris.length; i++) {
uris[i] = Uri.fromFile(new File(paths.get(i)));
}
filePathCallback.onReceiveValue(uris);
}
});
return true;
}
filePathCallback.onReceiveValue(null);
return true;
}

@Override
public void onProgressChanged(WebView view, int progress) {
if (flutterApi != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,10 @@ abstract class WebChromeClient {

/// Notify the host application that a file should be downloaded.
void onProgressChanged(WebView webView, int progress) {}

Future<List<String>> onShowFileChooser(WebView webView) async {
return [];
}
}

/// Encompasses parameters to the [WebViewClient.requestLoading] method.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// 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.

// Autogenerated from Pigeon (v1.0.9), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
Expand Down Expand Up @@ -1810,6 +1806,8 @@ abstract class WebChromeClientFlutterApi {

void dispose(int instanceId);
void onProgressChanged(int instanceId, int webViewInstanceId, int progress);
Future<List<String?>> onShowFileChooser(
int instanceId, int webViewInstanceId);
static void setup(WebChromeClientFlutterApi? api,
{BinaryMessenger? binaryMessenger}) {
{
Expand Down Expand Up @@ -1858,6 +1856,30 @@ abstract class WebChromeClientFlutterApi {
});
}
}
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
channel.setMessageHandler(null);
} else {
channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_instanceId = (args[0] as int?);
assert(arg_instanceId != null,
'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser was null, expected non-null int.');
final int? arg_webViewInstanceId = (args[1] as int?);
assert(arg_webViewInstanceId != null,
'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser was null, expected non-null int.');
final List<String?> output = await api.onShowFileChooser(
arg_instanceId!, arg_webViewInstanceId!);
return output;
});
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,25 @@ class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
);
instance!.onProgressChanged(webViewInstance!, progress);
}

@override
Future<List<String>> onShowFileChooser(
int instanceId, int webViewInstanceId) async {
final WebChromeClient? instance =
instanceManager.getInstance(instanceId) as WebChromeClient?;
final WebView? webViewInstance =
instanceManager.getInstance(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebChromeClient with instanceId: $instanceId',
);
assert(
webViewInstance != null,
'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
);
// AFAIRE
return await instance!.onShowFileChooser(webViewInstance!);
}
}

/// Host api implementation for [WebStorage].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ class WebViewAndroidPlatformController extends WebViewPlatformController {
}

Future<void> _setHasProgressTracking(bool hasProgressTracking) async {
webChromeClient._onShowFileChooser = callbacksHandler.onShowFileChooser;
if (hasProgressTracking) {
webChromeClient._onProgress = callbacksHandler.onProgress;
} else {
Expand Down Expand Up @@ -690,13 +691,23 @@ class WebViewAndroidWebViewClient extends android_webview.WebViewClient {
class WebViewAndroidWebChromeClient extends android_webview.WebChromeClient {
// Changed by WebViewAndroidPlatformController.
void Function(int progress)? _onProgress;
Future<List<String>> Function()? _onShowFileChooser;

@override
void onProgressChanged(android_webview.WebView webView, int progress) {
if (_onProgress != null) {
_onProgress!(progress);
}
}

@override
Future<List<String>> onShowFileChooser(
android_webview.WebView webView) async {
if (_onShowFileChooser != null) {
return await _onShowFileChooser!();
}
return [];
}
}

/// Handles constructing [android_webview.WebView]s and calling static methods.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ abstract class WebChromeClientFlutterApi {
void dispose(int instanceId);

void onProgressChanged(int instanceId, int webViewInstanceId, int progress);

@async
List<String> onShowFileChooser(int instanceId, int webViewInstanceId);
}

@HostApi(dartHostTestHandler: 'TestWebStorageHostApi')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// 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.

// Autogenerated from Pigeon (v1.0.9), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import
Expand All @@ -12,7 +8,7 @@ import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:webview_flutter_android/src/android_webview.pigeon.dart';
import '../lib/src/android_webview.pigeon.dart';

class _TestWebViewHostApiCodec extends StandardMessageCodec {
const _TestWebViewHostApiCodec();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
case 'onProgress':
_platformCallbacksHandler.onProgress(call.arguments['progress'] as int);
return null;
//case 'onShowFileChooser':
//return await _platformCallbacksHandler.onShowFileChooser();
case 'onPageStarted':
_platformCallbacksHandler
.onPageStarted(call.arguments['url']! as String);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ abstract class WebViewPlatformCallbacksHandler {
/// /// Only works when [WebSettings.hasProgressTracking] is set to `true`.
void onProgress(int progress);

Future<List<String>> onShowFileChooser();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change as things are currently structured, so adding this will need to wait for the platform interface API update.

@mvanbeusekom @bparrishMines Can we verify that this kind of change will indeed by safe in the updated version?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I don't see any reason this won't be possible with the new interface. The only difference is that we may make this unique to AndroidWebViewControllerDelegate. Which would only slightly change how it is used in this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bparrishMines what's the branch of the "new interface" ?
I understand that you are refactoring webview_flutter and you want to be sure that this PR will fit the new design.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The work on the new interface can be tracked at: flutter/flutter#94051.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, in the meantime, this PR is stale right ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all of it. The part that adds it to the native api WebChromeClient wrapper should remain valid. Reducing the PR to this will make it easier to add it to the new interface at a later point.


/// Report web resource loading error to the host application.
void onWebResourceError(WebResourceError error);
}