Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[webview_flutter_android] Added the functionality to fullscreen html5 video #3879

Merged
merged 64 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
1bbd61f
Added fullscreen capabilities to the android webview
paulppn May 2, 2023
279a678
Fixed the formatting
paulppn May 2, 2023
6649aa8
Fixed some more formatting + added a nonnull
paulppn May 2, 2023
fbc3f57
Removed empty line
paulppn May 2, 2023
06935c9
Adjusted another nonnull
paulppn May 2, 2023
ff9137f
Fixed two lint warnings
paulppn May 2, 2023
bef6c2a
customviewcallback dart impl
bparrishMines May 30, 2023
0cd62d0
view dart impl
bparrishMines May 30, 2023
6ca452a
Merge branch 'main' of github.com:flutter/packages into feature/add_f…
bparrishMines May 30, 2023
ba03b6b
formatting
bparrishMines May 30, 2023
9f0dbe1
dart side of webchromeclient wrap
bparrishMines May 30, 2023
e9295b2
implement java side
bparrishMines May 30, 2023
d547039
fix java side
bparrishMines May 31, 2023
59a0da4
formatting
bparrishMines May 31, 2023
f3693b8
fix pubspec
bparrishMines May 31, 2023
47c7d98
Merge branch 'main' of github.com:flutter/packages into feature/add_f…
bparrishMines May 31, 2023
8151750
remove unneeded lines
bparrishMines May 31, 2023
759f19b
remove unneeded mocks
bparrishMines May 31, 2023
bdb0806
fix lint
bparrishMines May 31, 2023
47e3108
Merge remote-tracking branch 'origin/main' into feature/add_fullscree…
mvanbeusekom Jul 5, 2023
5ea042d
Adds Dart implementation fullscreen video
mvanbeusekom Jul 5, 2023
216757e
Adds Android fullscreen functionality
mvanbeusekom Jul 6, 2023
4ff9e75
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Jul 6, 2023
76c02cc
Merge remote-tracking branch 'origin/main' into feature/add_fullscree…
mvanbeusekom Jul 7, 2023
b899cb0
Merge branch 'feature/add_fullscreen_video' of github.com:paulppn/flu…
mvanbeusekom Jul 7, 2023
24bc43e
Merge remote-tracking branch 'origin/main' into feature/add_fullscree…
mvanbeusekom Jul 11, 2023
afe0b8f
Apply review feedback
mvanbeusekom Jul 11, 2023
e9d418a
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Jul 11, 2023
e347a49
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Jul 12, 2023
ab876f2
Applied PR feedback
mvanbeusekom Jul 12, 2023
9323791
Merge branch 'feature/add_fullscreen_video' of github.com:paulppn/flu…
mvanbeusekom Jul 12, 2023
4cb8746
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Jul 12, 2023
8baca49
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Jul 12, 2023
f385bcc
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Jul 13, 2023
178c5e7
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Jul 19, 2023
37edbf1
Merge remote-tracking branch 'upstream/main' into feature/add_fullscr…
mvanbeusekom Aug 2, 2023
7219770
Merge branch 'feature/add_fullscreen_video' of github.com:paulppn/flu…
mvanbeusekom Aug 2, 2023
c612ef5
Adds integration test to validate fullscreen
mvanbeusekom Aug 3, 2023
9d06cae
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Aug 3, 2023
041441d
Ensure the CustomViewHostApi is setup correctly
mvanbeusekom Aug 3, 2023
d4359d5
Merge branch 'feature/add_fullscreen_video' of github.com:paulppn/flu…
mvanbeusekom Aug 3, 2023
5169000
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Aug 3, 2023
87443f5
Merge remote-tracking branch 'upstream/main' into feature/add_fullscr…
mvanbeusekom Aug 10, 2023
1f1ee82
Merge branch 'main' of https://github.com/flutter/packages into featu…
mvanbeusekom Aug 11, 2023
5ef97bc
Remove obsolete file
mvanbeusekom Aug 11, 2023
ba39519
Merge branch 'main' into feature/add_fullscreen_video
mvanbeusekom Aug 11, 2023
269ad02
Merge branch 'feature/add_fullscreen_video' of github.com:paulppn/flu…
mvanbeusekom Aug 11, 2023
6347f98
Fixed analysis warning
mvanbeusekom Aug 11, 2023
b319604
Formatted JAVA files
mvanbeusekom Aug 14, 2023
aa35251
Merge remote-tracking branch 'upstream/main' into feature/add_fullscr…
mvanbeusekom Aug 15, 2023
d9f9812
Merge branch 'main' of https://github.com/flutter/packages into featu…
mvanbeusekom Aug 17, 2023
3e39dd2
Explain the tapAt statement in integration test
mvanbeusekom Aug 17, 2023
3e74540
Update packages/webview_flutter/webview_flutter_android/example/integ…
bparrishMines Aug 18, 2023
289cccd
update dartdocs and changelog
bparrishMines Aug 18, 2023
1e63109
Merge branch 'main' of github.com:flutter/packages into feature/add_f…
bparrishMines Aug 18, 2023
5d45ea0
provide example for fullscreen
bparrishMines Aug 18, 2023
6ca8d15
require callbacks
bparrishMines Aug 18, 2023
0b7361a
formatting
bparrishMines Aug 21, 2023
cdcff55
Merge branch 'main' of github.com:flutter/packages into feature/add_f…
bparrishMines Aug 21, 2023
45720e4
add a default handler
bparrishMines Aug 25, 2023
67d660d
Merge branch 'main' of github.com:flutter/packages into feature/add_f…
bparrishMines Aug 25, 2023
acfd11d
add integration test
bparrishMines Aug 25, 2023
7744529
switch to unit test
bparrishMines Sep 7, 2023
1a65e0f
Merge branch 'main' of github.com:flutter/packages into feature/add_f…
bparrishMines Sep 7, 2023
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## NEXT
## 3.10.0

* Adds support for playing video in fullscreen. See
`AndroidWebViewController.setCustomWidgetCallbacks`.
* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19.

## 3.9.3
Expand Down
23 changes: 22 additions & 1 deletion packages/webview_flutter/webview_flutter_android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ This can be configured for versions >=23 with
`AndroidWebViewWidgetCreationParams.displayWithHybridComposition`. See https://pub.dev/packages/webview_flutter#platform-specific-features
for more details on setting platform-specific features in the main plugin.

### External Native API
## External Native API
Copy link
Contributor

Choose a reason for hiding this comment

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

Wasn't this correct before? This section seems to be Android-only as written.


The plugin also provides a native API accessible by the native code of Android applications or
packages. This API follows the convention of breaking changes of the Dart API, which means that any
Expand All @@ -52,6 +52,27 @@ Java:
import io.flutter.plugins.webviewflutter.WebViewFlutterAndroidExternalApi;
```

## Fullscreen Video

To display a video as fullscreen, an app must manually handle the notification that the current page
has entered fullscreen mode. This can be done by calling
`AndroidWebViewController.setCustomWidgetCallbacks`. Below is an example implementation.

<?code-excerpt "example/lib/main.dart (fullscreen_example)"?>
```dart
androidController.setCustomWidgetCallbacks(
onShowCustomWidget: (Widget widget, OnHideCustomWidgetCallback callback) {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) => widget,
fullscreenDialog: true,
));
},
onHideCustomWidget: () {
Navigator.of(context).pop();
},
);
```

## Contributing

This package uses [pigeon][3] to generate the communication layer between Flutter and the host
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.

package io.flutter.plugins.webviewflutter;

import android.webkit.WebChromeClient.CustomViewCallback;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.CustomViewCallbackFlutterApi;

/**
* Flutter API implementation for `CustomViewCallback`.
*
* <p>This class may handle adding native instances that are attached to a Dart instance or passing
* arguments of callbacks methods to a Dart instance.
*/
public class CustomViewCallbackFlutterApiImpl {
// To ease adding additional methods, this value is added prematurely.
@SuppressWarnings({"unused", "FieldCanBeLocal"})
private final BinaryMessenger binaryMessenger;

private final InstanceManager instanceManager;
private CustomViewCallbackFlutterApi api;

/**
* Constructs a {@link CustomViewCallbackFlutterApiImpl}.
*
* @param binaryMessenger used to communicate with Dart over asynchronous messages
* @param instanceManager maintains instances stored to communicate with attached Dart objects
*/
public CustomViewCallbackFlutterApiImpl(
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
api = new CustomViewCallbackFlutterApi(binaryMessenger);
}

/**
* Stores the `CustomViewCallback` instance and notifies Dart to create and store a new
* `CustomViewCallback` instance that is attached to this one. If `instance` has already been
* added, this method does nothing.
*/
public void create(
@NonNull CustomViewCallback instance,
@NonNull CustomViewCallbackFlutterApi.Reply<Void> callback) {
if (!instanceManager.containsInstance(instance)) {
api.create(instanceManager.addHostCreatedInstance(instance), callback);
}
}

/**
* Sets the Flutter API used to send messages to Dart.
*
* <p>This is only visible for testing.
*/
@VisibleForTesting
void setApi(@NonNull CustomViewCallbackFlutterApi api) {
this.api = api;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.

package io.flutter.plugins.webviewflutter;

import android.webkit.WebChromeClient.CustomViewCallback;
import androidx.annotation.NonNull;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.CustomViewCallbackHostApi;
import java.util.Objects;

/**
* Host API implementation for `CustomViewCallback`.
*
* <p>This class may handle instantiating and adding native object instances that are attached to a
* Dart instance or handle method calls on the associated native class or an instance of the class.
*/
public class CustomViewCallbackHostApiImpl implements CustomViewCallbackHostApi {
// To ease adding additional methods, this value is added prematurely.
@SuppressWarnings({"unused", "FieldCanBeLocal"})
private final BinaryMessenger binaryMessenger;

private final InstanceManager instanceManager;

/**
* Constructs a {@link CustomViewCallbackHostApiImpl}.
*
* @param binaryMessenger used to communicate with Dart over asynchronous messages
* @param instanceManager maintains instances stored to communicate with attached Dart objects
*/
public CustomViewCallbackHostApiImpl(
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
}

@Override
public void onCustomViewHidden(@NonNull Long identifier) {
getCustomViewCallbackInstance(identifier).onCustomViewHidden();
}

private CustomViewCallback getCustomViewCallbackInstance(@NonNull Long identifier) {
return Objects.requireNonNull(instanceManager.getInstance(identifier));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
package io.flutter.plugins.webviewflutter;

import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;

class FlutterWebViewFactory extends PlatformViewFactory {
class FlutterViewFactory extends PlatformViewFactory {
private final InstanceManager instanceManager;

FlutterWebViewFactory(InstanceManager instanceManager) {
FlutterViewFactory(InstanceManager instanceManager) {
super(StandardMessageCodec.INSTANCE);
this.instanceManager = instanceManager;
}
Expand All @@ -24,13 +25,26 @@ class FlutterWebViewFactory extends PlatformViewFactory {
public PlatformView create(Context context, int viewId, @Nullable Object args) {
final Integer identifier = (Integer) args;
if (identifier == null) {
throw new IllegalStateException("An identifier is required to retrieve WebView instance.");
throw new IllegalStateException("An identifier is required to retrieve a View instance.");
}

final PlatformView view = instanceManager.getInstance(identifier);
if (view == null) {
throw new IllegalStateException("Unable to find WebView instance: " + args);
final Object instance = instanceManager.getInstance(identifier);

if (instance instanceof PlatformView) {
return (PlatformView) instance;
} else if (instance instanceof View) {
return new PlatformView() {
@Override
public View getView() {
return (View) instance;
}

@Override
public void dispose() {}
};
}
return view;

throw new IllegalStateException(
"Unable to find a PlatformView or View instance: " + args + ", " + instance);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2589,6 +2589,33 @@ public void onPermissionRequest(
new ArrayList<Object>(Arrays.asList(instanceIdArg, requestInstanceIdArg)),
channelReply -> callback.reply(null));
}
/** Callback to Dart function `WebChromeClient.onShowCustomView`. */
public void onShowCustomView(
@NonNull Long instanceIdArg,
@NonNull Long viewIdentifierArg,
@NonNull Long callbackIdentifierArg,
@NonNull Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.WebChromeClientFlutterApi.onShowCustomView",
getCodec());
channel.send(
new ArrayList<Object>(
Arrays.asList(instanceIdArg, viewIdentifierArg, callbackIdentifierArg)),
channelReply -> callback.reply(null));
}
/** Callback to Dart function `WebChromeClient.onHideCustomView`. */
public void onHideCustomView(@NonNull Long instanceIdArg, @NonNull Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.WebChromeClientFlutterApi.onHideCustomView",
getCodec());
channel.send(
new ArrayList<Object>(Collections.singletonList(instanceIdArg)),
channelReply -> callback.reply(null));
}
/** Callback to Dart function `WebChromeClient.onGeolocationPermissionsShowPrompt`. */
public void onGeolocationPermissionsShowPrompt(
@NonNull Long instanceIdArg,
Expand Down Expand Up @@ -2866,6 +2893,135 @@ public void create(
channelReply -> callback.reply(null));
}
}
/**
* Host API for `CustomViewCallback`.
*
* <p>This class may handle instantiating and adding native object instances that are attached to
* a Dart instance or handle method calls on the associated native class or an instance of the
* class.
*
* <p>See
* https://developer.android.com/reference/android/webkit/WebChromeClient.CustomViewCallback.
*
* <p>Generated interface from Pigeon that represents a handler of messages from Flutter.
*/
public interface CustomViewCallbackHostApi {
/** Handles Dart method `CustomViewCallback.onCustomViewHidden`. */
void onCustomViewHidden(@NonNull Long identifier);

/** The codec used by CustomViewCallbackHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
}
/**
* Sets up an instance of `CustomViewCallbackHostApi` to handle messages through the
* `binaryMessenger`.
*/
static void setup(
@NonNull BinaryMessenger binaryMessenger, @Nullable CustomViewCallbackHostApi api) {
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.CustomViewCallbackHostApi.onCustomViewHidden",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number identifierArg = (Number) args.get(0);
try {
api.onCustomViewHidden(
(identifierArg == null) ? null : identifierArg.longValue());
wrapped.add(0, null);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/**
* Flutter API for `CustomViewCallback`.
*
* <p>This class may handle instantiating and adding Dart instances that are attached to a native
* instance or receiving callback methods from an overridden native class.
*
* <p>See
* https://developer.android.com/reference/android/webkit/WebChromeClient.CustomViewCallback.
*
* <p>Generated class from Pigeon that represents Flutter messages that can be called from Java.
*/
public static class CustomViewCallbackFlutterApi {
private final @NonNull BinaryMessenger binaryMessenger;

public CustomViewCallbackFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) {
this.binaryMessenger = argBinaryMessenger;
}

/** Public interface for sending reply. */
@SuppressWarnings("UnknownNullness")
public interface Reply<T> {
void reply(T reply);
}
/** The codec used by CustomViewCallbackFlutterApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
}
/** Create a new Dart instance and add it to the `InstanceManager`. */
public void create(@NonNull Long identifierArg, @NonNull Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.CustomViewCallbackFlutterApi.create",
getCodec());
channel.send(
new ArrayList<Object>(Collections.singletonList(identifierArg)),
channelReply -> callback.reply(null));
}
}
/**
* Flutter API for `View`.
*
* <p>This class may handle instantiating and adding Dart instances that are attached to a native
* instance or receiving callback methods from an overridden native class.
*
* <p>See https://developer.android.com/reference/android/view/View.
*
* <p>Generated class from Pigeon that represents Flutter messages that can be called from Java.
*/
public static class ViewFlutterApi {
private final @NonNull BinaryMessenger binaryMessenger;

public ViewFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) {
this.binaryMessenger = argBinaryMessenger;
}

/** Public interface for sending reply. */
@SuppressWarnings("UnknownNullness")
public interface Reply<T> {
void reply(T reply);
}
/** The codec used by ViewFlutterApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
}
/** Create a new Dart instance and add it to the `InstanceManager`. */
public void create(@NonNull Long identifierArg, @NonNull Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.ViewFlutterApi.create", getCodec());
channel.send(
new ArrayList<Object>(Collections.singletonList(identifierArg)),
channelReply -> callback.reply(null));
}
}
/**
* Host API for `GeolocationPermissionsCallback`.
*
Expand Down
Loading