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

[webview_flutter] Android implementation of loadFlutterAsset method. #4581

Merged
Merged
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
4 changes: 4 additions & 0 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.6.0

* Adds implementation of the `loadFlutterAsset` method from the platform interface.

## 2.5.0

* Adds an option to set the background color of the webview.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,4 @@ android {
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// 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.content.res.AssetManager;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.PluginRegistry;
import java.io.IOException;

/** Provides access to the assets registered as part of the App bundle. */
abstract class FlutterAssetManager {
final AssetManager assetManager;

/**
* Constructs a new instance of the {@link FlutterAssetManager}.
*
* @param assetManager Instance of Android's {@link AssetManager} used to access assets within the
* App bundle.
*/
public FlutterAssetManager(AssetManager assetManager) {
this.assetManager = assetManager;
}

/**
* Gets the relative file path to the Flutter asset with the given name, including the file's
* extension, e.g., "myImage.jpg".
*
* <p>The returned file path is relative to the Android app's standard asset's directory.
* Therefore, the returned path is appropriate to pass to Android's AssetManager, but the path is
* not appropriate to load as an absolute path.
*/
abstract String getAssetFilePathByName(String name);

/**
* Returns a String array of all the assets at the given path.
*
* @param path A relative path within the assets, i.e., "docs/home.html". This value cannot be
* null.
* @return String[] Array of strings, one for each asset. These file names are relative to 'path'.
* This value may be null.
* @throws IOException Throws an IOException in case I/O operations were interrupted.
*/
public String[] list(@NonNull String path) throws IOException {
return assetManager.list(path);
}

/**
* Provides access to assets using the {@link PluginRegistry.Registrar} for looking up file paths
* to Flutter assets.
*
* @deprecated The {@link RegistrarFlutterAssetManager} is for Flutter's v1 embedding. For
* instructions on migrating a plugin from Flutter's v1 Android embedding to v2, visit
* http://flutter.dev/go/android-plugin-migration
*/
@Deprecated
static class RegistrarFlutterAssetManager extends FlutterAssetManager {
final PluginRegistry.Registrar registrar;

/**
* Constructs a new instance of the {@link RegistrarFlutterAssetManager}.
*
* @param assetManager Instance of Android's {@link AssetManager} used to access assets within
* the App bundle.
* @param registrar Instance of {@link io.flutter.plugin.common.PluginRegistry.Registrar} used
* to look up file paths to assets registered by Flutter.
*/
RegistrarFlutterAssetManager(AssetManager assetManager, PluginRegistry.Registrar registrar) {
super(assetManager);
this.registrar = registrar;
}

@Override
public String getAssetFilePathByName(String name) {
return registrar.lookupKeyForAsset(name);
}
}

/**
* Provides access to assets using the {@link FlutterPlugin.FlutterAssets} for looking up file
* paths to Flutter assets.
*/
static class PluginBindingFlutterAssetManager extends FlutterAssetManager {
final FlutterPlugin.FlutterAssets flutterAssets;

/**
* Constructs a new instance of the {@link PluginBindingFlutterAssetManager}.
*
* @param assetManager Instance of Android's {@link AssetManager} used to access assets within
* the App bundle.
* @param flutterAssets Instance of {@link
* io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterAssets} used to look up file
* paths to assets registered by Flutter.
*/
PluginBindingFlutterAssetManager(
AssetManager assetManager, FlutterPlugin.FlutterAssets flutterAssets) {
super(assetManager);
this.flutterAssets = flutterAssets;
}

@Override
public String getAssetFilePathByName(String name) {
return flutterAssets.getAssetFilePathByName(name);
}
}
}
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.WebView;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.FlutterAssetManagerHostApi;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Host api implementation for {@link WebView}.
*
* <p>Handles creating {@link WebView}s that intercommunicate with a paired Dart object.
*/
public class FlutterAssetManagerHostApiImpl implements FlutterAssetManagerHostApi {
final FlutterAssetManager flutterAssetManager;

/** Constructs a new instance of {@link FlutterAssetManagerHostApiImpl}. */
public FlutterAssetManagerHostApiImpl(FlutterAssetManager flutterAssetManager) {
this.flutterAssetManager = flutterAssetManager;
}

@Override
public List<String> list(String path) {
try {
String[] paths = flutterAssetManager.list(path);

if (paths == null) {
return new ArrayList<>();
}

return Arrays.asList(paths);
} catch (IOException ex) {
throw new RuntimeException(ex.getMessage());
}
}

@Override
public String getAssetFilePathByName(String name) {
return flutterAssetManager.getAssetFilePathByName(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** Generated class from Pigeon. */
Expand Down Expand Up @@ -1915,6 +1916,84 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) {
}
}

private static class FlutterAssetManagerHostApiCodec extends StandardMessageCodec {
public static final FlutterAssetManagerHostApiCodec INSTANCE =
new FlutterAssetManagerHostApiCodec();

private FlutterAssetManagerHostApiCodec() {}
}

/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface FlutterAssetManagerHostApi {
List<String> list(String path);

String getAssetFilePathByName(String name);

/** The codec used by FlutterAssetManagerHostApi. */
static MessageCodec<Object> getCodec() {
return FlutterAssetManagerHostApiCodec.INSTANCE;
}

/**
* Sets up an instance of `FlutterAssetManagerHostApi` to handle messages through the
* `binaryMessenger`.
*/
static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi api) {
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.FlutterAssetManagerHostApi.list", getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
Map<String, Object> wrapped = new HashMap<>();
try {
ArrayList<Object> args = (ArrayList<Object>) message;
String pathArg = (String) args.get(0);
if (pathArg == null) {
throw new NullPointerException("pathArg unexpectedly null.");
}
List<String> output = api.list(pathArg);
wrapped.put("result", output);
} catch (Error | RuntimeException exception) {
wrapped.put("error", wrapError(exception));
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
Map<String, Object> wrapped = new HashMap<>();
try {
ArrayList<Object> args = (ArrayList<Object>) message;
String nameArg = (String) args.get(0);
if (nameArg == null) {
throw new NullPointerException("nameArg unexpectedly null.");
}
String output = api.getAssetFilePathByName(nameArg);
wrapped.put("result", output);
} catch (Error | RuntimeException exception) {
wrapped.put("error", wrapError(exception));
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}

private static class WebChromeClientFlutterApiCodec extends StandardMessageCodec {
public static final WebChromeClientFlutterApiCodec INSTANCE =
new WebChromeClientFlutterApiCodec();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerHostApi;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.FlutterAssetManagerHostApi;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelHostApi;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebSettingsHostApi;
Expand Down Expand Up @@ -61,15 +62,18 @@ public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registra
registrar.messenger(),
registrar.platformViewRegistry(),
registrar.activity(),
registrar.view());
registrar.view(),
new FlutterAssetManager.RegistrarFlutterAssetManager(
registrar.context().getAssets(), registrar));
new FlutterCookieManager(registrar.messenger());
}

private void setUp(
BinaryMessenger binaryMessenger,
PlatformViewRegistry viewRegistry,
Context context,
View containerView) {
View containerView,
FlutterAssetManager flutterAssetManager) {
new FlutterCookieManager(binaryMessenger);

InstanceManager instanceManager = new InstanceManager();
Expand Down Expand Up @@ -111,6 +115,8 @@ private void setUp(
binaryMessenger,
new WebSettingsHostApiImpl(
instanceManager, new WebSettingsHostApiImpl.WebSettingsCreator()));
FlutterAssetManagerHostApi.setup(
binaryMessenger, new FlutterAssetManagerHostApiImpl(flutterAssetManager));
}

@Override
Expand All @@ -120,7 +126,9 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
binding.getBinaryMessenger(),
binding.getPlatformViewRegistry(),
binding.getApplicationContext(),
null);
null,
new FlutterAssetManager.PluginBindingFlutterAssetManager(
binding.getApplicationContext().getAssets(), binding.getFlutterAssets()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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 static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;

public class FlutterAssetManagerHostApiImplTest {
@Mock FlutterAssetManager mockFlutterAssetManager;

FlutterAssetManagerHostApiImpl testFlutterAssetManagerHostApiImpl;

@Before
public void setUp() {
mockFlutterAssetManager = mock(FlutterAssetManager.class);

testFlutterAssetManagerHostApiImpl =
new FlutterAssetManagerHostApiImpl(mockFlutterAssetManager);
}

@Test
public void list() {
try {
when(mockFlutterAssetManager.list("test/path"))
.thenReturn(new String[] {"index.html", "styles.css"});
List<String> actualFilePaths = testFlutterAssetManagerHostApiImpl.list("test/path");
verify(mockFlutterAssetManager).list("test/path");
assertArrayEquals(new String[] {"index.html", "styles.css"}, actualFilePaths.toArray());
} catch (IOException ex) {
fail();
}
}

@Test
public void list_returns_empty_list_when_no_results() {
try {
when(mockFlutterAssetManager.list("test/path")).thenReturn(null);
List<String> actualFilePaths = testFlutterAssetManagerHostApiImpl.list("test/path");
verify(mockFlutterAssetManager).list("test/path");
assertArrayEquals(new String[] {}, actualFilePaths.toArray());
} catch (IOException ex) {
fail();
}
}

@Test(expected = RuntimeException.class)
public void list_should_convert_io_exception_to_runtime_exception() {
try {
when(mockFlutterAssetManager.list("test/path")).thenThrow(new IOException());
testFlutterAssetManagerHostApiImpl.list("test/path");
} catch (IOException ex) {
fail();
}
}

@Test
public void getAssetFilePathByName() {
when(mockFlutterAssetManager.getAssetFilePathByName("index.html"))
.thenReturn("flutter_assets/index.html");
String filePath = testFlutterAssetManagerHostApiImpl.getAssetFilePathByName("index.html");
verify(mockFlutterAssetManager).getAssetFilePathByName("index.html");
assertEquals("flutter_assets/index.html", filePath);
}
}
Loading