Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
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
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/FlutterEngineCacheTest.java",
"test/io/flutter/embedding/engine/FlutterEngineTest.java",
"test/io/flutter/embedding/engine/FlutterJNITest.java",
"test/io/flutter/embedding/engine/PluginComponentTest.java",
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
"test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ public FlutterEngine(

this.pluginRegistry = new FlutterEnginePluginRegistry(
context.getApplicationContext(),
this
this,
flutterLoader
);

if (automaticallyRegisterPlugins) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Set;

import io.flutter.Log;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.PluginRegistry;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
Expand Down Expand Up @@ -90,15 +91,17 @@ class FlutterEnginePluginRegistry implements PluginRegistry,

FlutterEnginePluginRegistry(
@NonNull Context appContext,
@NonNull FlutterEngine flutterEngine
@NonNull FlutterEngine flutterEngine,
@NonNull FlutterLoader flutterLoader
) {
this.flutterEngine = flutterEngine;
pluginBinding = new FlutterPlugin.FlutterPluginBinding(
appContext,
flutterEngine,
flutterEngine.getDartExecutor(),
flutterEngine.getRenderer(),
flutterEngine.getPlatformViewsController().getRegistry()
flutterEngine.getPlatformViewsController().getRegistry(),
new DefaultFlutterAssets(flutterLoader)
);
}

Expand Down Expand Up @@ -532,6 +535,30 @@ public void detachFromContentProvider() {
}
//----- End ContentProviderControlSurface -----

private static class DefaultFlutterAssets implements FlutterPlugin.FlutterAssets {
final FlutterLoader flutterLoader;

private DefaultFlutterAssets(@NonNull FlutterLoader flutterLoader) {
this.flutterLoader = flutterLoader;
}

public String getAssetFilePathByName(@NonNull String assetFileName) {
return flutterLoader.getLookupKeyForAsset(assetFileName);
}

public String getAssetFilePathByName(@NonNull String assetFileName, @NonNull String packageName) {
return flutterLoader.getLookupKeyForAsset(assetFileName, packageName);
}

public String getAssetFilePathBySubpath(@NonNull String assetSubpath) {
return flutterLoader.getLookupKeyForAsset(assetSubpath);
}

public String getAssetFilePathBySubpath(@NonNull String assetSubpath, @NonNull String packageName) {
return flutterLoader.getLookupKeyForAsset(assetSubpath, packageName);
}
}

private static class FlutterEngineActivityPluginBinding implements ActivityPluginBinding {
@NonNull
private final Activity activity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.support.annotation.NonNull;

import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.view.TextureRegistry;
Expand Down Expand Up @@ -101,19 +102,22 @@ class FlutterPluginBinding {
private final BinaryMessenger binaryMessenger;
private final TextureRegistry textureRegistry;
private final PlatformViewRegistry platformViewRegistry;
private final FlutterAssets flutterAssets;

public FlutterPluginBinding(
@NonNull Context applicationContext,
@NonNull FlutterEngine flutterEngine,
@NonNull BinaryMessenger binaryMessenger,
@NonNull TextureRegistry textureRegistry,
@NonNull PlatformViewRegistry platformViewRegistry
@NonNull PlatformViewRegistry platformViewRegistry,
@NonNull FlutterAssets flutterAssets
) {
this.applicationContext = applicationContext;
this.flutterEngine = flutterEngine;
this.binaryMessenger = binaryMessenger;
this.textureRegistry = textureRegistry;
this.platformViewRegistry = platformViewRegistry;
this.flutterAssets = flutterAssets;
}

@NonNull
Expand Down Expand Up @@ -146,5 +150,47 @@ public TextureRegistry getTextureRegistry() {
public PlatformViewRegistry getPlatformViewRegistry() {
return platformViewRegistry;
}

@NonNull
public FlutterAssets getFlutterAssets() {
return flutterAssets;
}
}

/**
* Provides Flutter plugins with access to Flutter asset information.
*/
interface FlutterAssets {
/**
* Returns the relative file path to the Flutter asset with the given name, including the file's
* extension, e.g., {@code "myImage.jpg"}.
*
* <p>The returned file path is relative to the Android app's standard assets directory.
* Therefore, the returned path is appropriate to pass to Android's {@code AssetManager},
* but the path is not appropriate to load as an absolute path.
*/
String getAssetFilePathByName(@NonNull String assetFileName);

/**
* Same as {@link #getAssetFilePathByName(String)} but with added support for an explicit
* Android {@code packageName}.
*/
String getAssetFilePathByName(@NonNull String assetFileName, @NonNull String packageName);

/**
* Returns the relative file path to the Flutter asset with the given subpath, including the file's
* extension, e.g., {@code "/dir1/dir2/myImage.jpg"}.
*
* <p>The returned file path is relative to the Android app's standard assets directory.
* Therefore, the returned path is appropriate to pass to Android's {@code AssetManager},
* but the path is not appropriate to load as an absolute path.
*/
String getAssetFilePathBySubpath(@NonNull String assetSubpath);

/**
* Same as {@link #getAssetFilePathBySubpath(String)} but with added support for an explicit
* Android {@code packageName}.
*/
String getAssetFilePathBySubpath(@NonNull String assetSubpath, @NonNull String packageName);
}
}
2 changes: 2 additions & 0 deletions shell/platform/android/test/io/flutter/FlutterTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.flutter.plugin.platform.SingleViewPresentationTest;
import io.flutter.util.PreconditionsTest;
import test.io.flutter.embedding.engine.FlutterEngineTest;
import test.io.flutter.embedding.engine.PluginComponentTest;
import test.io.flutter.embedding.engine.dart.DartExecutorTest;

@RunWith(Suite.class)
Expand All @@ -38,6 +39,7 @@
FlutterRendererTest.class,
FlutterViewTest.class,
PlatformChannelTest.class,
PluginComponentTest.class,
PreconditionsTest.class,
RenderingComponentTest.class,
StandardMessageCodecTest.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package test.io.flutter.embedding.engine;

import android.support.annotation.NonNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.FlutterPlugin;

import static junit.framework.TestCase.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@Config(manifest=Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class PluginComponentTest {
@Test
public void pluginsCanAccessFlutterAssetPaths() {
// Setup test.
FlutterJNI flutterJNI = mock(FlutterJNI.class);
when(flutterJNI.isAttached()).thenReturn(true);

// FlutterLoader is the object to which the PluginRegistry defers for obtaining
// the path to a Flutter asset. Ideally in this component test we would use a
// real FlutterLoader and directly verify the relationship between FlutterAssets
// and FlutterLoader. However, a real FlutterLoader cannot be used in a JVM test
// because it would attempt to load native libraries. Therefore, we create a fake
// FlutterLoader, but then we defer the corresponding asset lookup methods to the
// real FlutterLoader singleton. This test ends up verifying that when FlutterAssets
// is queried for an asset path, it returns the real expected path based on real
// FlutterLoader behavior.
FlutterLoader flutterLoader = mock(FlutterLoader.class);
when(flutterLoader.getLookupKeyForAsset(any(String.class))).thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
// Defer to a real FlutterLoader to return the asset path.
String fileNameOrSubpath = (String) invocation.getArguments()[0];
return FlutterLoader.getInstance().getLookupKeyForAsset(fileNameOrSubpath);
}
});
when(flutterLoader.getLookupKeyForAsset(any(String.class), any(String.class))).thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
// Defer to a real FlutterLoader to return the asset path.
String fileNameOrSubpath = (String) invocation.getArguments()[0];
String packageName = (String) invocation.getArguments()[1];
return FlutterLoader.getInstance().getLookupKeyForAsset(fileNameOrSubpath, packageName);
}
});

// Execute behavior under test.
FlutterEngine flutterEngine = new FlutterEngine(
RuntimeEnvironment.application,
flutterLoader,
flutterJNI
);

// As soon as our plugin is registered it will look up asset paths and store them
// for our verification.
PluginThatAccessesAssets plugin = new PluginThatAccessesAssets();
flutterEngine.getPlugins().add(plugin);

// Verify results.
assertEquals("flutter_assets/fake_asset.jpg", plugin.getAssetPathBasedOnName());
assertEquals("flutter_assets/packages/fakepackage/fake_asset.jpg", plugin.getAssetPathBasedOnNameAndPackage());
assertEquals("flutter_assets/some/path/fake_asset.jpg", plugin.getAssetPathBasedOnSubpath());
assertEquals("flutter_assets/packages/fakepackage/some/path/fake_asset.jpg", plugin.getAssetPathBasedOnSubpathAndPackage());
}

private static class PluginThatAccessesAssets implements FlutterPlugin {
private String assetPathBasedOnName;
private String assetPathBasedOnNameAndPackage;
private String assetPathBasedOnSubpath;
private String assetPathBasedOnSubpathAndPackage;

public String getAssetPathBasedOnName() {
return assetPathBasedOnName;
}

public String getAssetPathBasedOnNameAndPackage() {
return assetPathBasedOnNameAndPackage;
}

public String getAssetPathBasedOnSubpath() {
return assetPathBasedOnSubpath;
}

public String getAssetPathBasedOnSubpathAndPackage() {
return assetPathBasedOnSubpathAndPackage;
}

@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
assetPathBasedOnName = binding
.getFlutterAssets()
.getAssetFilePathByName("fake_asset.jpg");

assetPathBasedOnNameAndPackage = binding
.getFlutterAssets()
.getAssetFilePathByName("fake_asset.jpg", "fakepackage");

assetPathBasedOnSubpath = binding
.getFlutterAssets()
.getAssetFilePathByName("some/path/fake_asset.jpg");

assetPathBasedOnSubpathAndPackage = binding
.getFlutterAssets()
.getAssetFilePathByName("some/path/fake_asset.jpg", "fakepackage");
}

@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {}
}
}