blob: fdc0511e217befca24429d9b9d84b08fe09e9882 [file] [log] [blame]
// Copyright 2017 The Chromium 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.videoplayer;
import android.content.Context;
import android.util.Log;
import android.util.LongSparseArray;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.view.FlutterMain;
import io.flutter.view.TextureRegistry;
/** Android platform implementation of the VideoPlayerPlugin. */
public class VideoPlayerPlugin implements MethodCallHandler, FlutterPlugin {
private static final String TAG = "VideoPlayerPlugin";
private final LongSparseArray<VideoPlayer> videoPlayers = new LongSparseArray<>();
private FlutterState flutterState;
/** Register this with the v2 embedding for the plugin to respond to lifecycle callbacks. */
public VideoPlayerPlugin() {}
private VideoPlayerPlugin(Registrar registrar) {
this.flutterState =
new FlutterState(
registrar.context(),
registrar.messenger(),
registrar::lookupKeyForAsset,
registrar::lookupKeyForAsset,
registrar.textures());
flutterState.startListening(this);
}
/** Registers this with the stable v1 embedding. Will not respond to lifecycle events. */
public static void registerWith(Registrar registrar) {
final VideoPlayerPlugin plugin = new VideoPlayerPlugin(registrar);
registrar.addViewDestroyListener(
view -> {
plugin.onDestroy();
return false; // We are not interested in assuming ownership of the NativeView.
});
}
@Override
public void onAttachedToEngine(FlutterPluginBinding binding) {
this.flutterState =
new FlutterState(
binding.getApplicationContext(),
binding.getFlutterEngine().getDartExecutor(),
FlutterMain::getLookupKeyForAsset,
FlutterMain::getLookupKeyForAsset,
binding.getFlutterEngine().getRenderer());
flutterState.startListening(this);
}
@Override
public void onDetachedFromEngine(FlutterPluginBinding binding) {
if (flutterState == null) {
Log.wtf(TAG, "Detached from the engine before registering to it.");
}
flutterState.stopListening();
flutterState = null;
}
private void disposeAllPlayers() {
for (int i = 0; i < videoPlayers.size(); i++) {
videoPlayers.valueAt(i).dispose();
}
videoPlayers.clear();
}
private void onDestroy() {
// The whole FlutterView is being destroyed. Here we release resources acquired for all
// instances
// of VideoPlayer. Once https://github.com/flutter/flutter/issues/19358 is resolved this may
// be replaced with just asserting that videoPlayers.isEmpty().
// https://github.com/flutter/flutter/issues/20989 tracks this.
disposeAllPlayers();
}
@Override
public void onMethodCall(MethodCall call, Result result) {
if (flutterState == null || flutterState.textureRegistry == null) {
result.error("no_activity", "video_player plugin requires a foreground activity", null);
return;
}
switch (call.method) {
case "init":
disposeAllPlayers();
break;
case "create":
{
TextureRegistry.SurfaceTextureEntry handle =
flutterState.textureRegistry.createSurfaceTexture();
EventChannel eventChannel =
new EventChannel(
flutterState.binaryMessenger, "flutter.io/videoPlayer/videoEvents" + handle.id());
VideoPlayer player;
if (call.argument("asset") != null) {
String assetLookupKey;
if (call.argument("package") != null) {
assetLookupKey =
flutterState.keyForAssetAndPackageName.get(
call.argument("asset"), call.argument("package"));
} else {
assetLookupKey = flutterState.keyForAsset.get(call.argument("asset"));
}
player =
new VideoPlayer(
flutterState.applicationContext,
eventChannel,
handle,
"asset:///" + assetLookupKey,
result,
null);
videoPlayers.put(handle.id(), player);
} else {
player =
new VideoPlayer(
flutterState.applicationContext,
eventChannel,
handle,
call.argument("uri"),
result,
call.argument("formatHint"));
videoPlayers.put(handle.id(), player);
}
break;
}
default:
{
long textureId = ((Number) call.argument("textureId")).longValue();
VideoPlayer player = videoPlayers.get(textureId);
if (player == null) {
result.error(
"Unknown textureId",
"No video player associated with texture id " + textureId,
null);
return;
}
onMethodCall(call, result, textureId, player);
break;
}
}
}
private void onMethodCall(MethodCall call, Result result, long textureId, VideoPlayer player) {
switch (call.method) {
case "setLooping":
player.setLooping(call.argument("looping"));
result.success(null);
break;
case "setVolume":
player.setVolume(call.argument("volume"));
result.success(null);
break;
case "play":
player.play();
result.success(null);
break;
case "pause":
player.pause();
result.success(null);
break;
case "seekTo":
int location = ((Number) call.argument("location")).intValue();
player.seekTo(location);
result.success(null);
break;
case "position":
result.success(player.getPosition());
player.sendBufferingUpdate();
break;
case "dispose":
player.dispose();
videoPlayers.remove(textureId);
result.success(null);
break;
default:
result.notImplemented();
break;
}
}
private interface KeyForAssetFn {
String get(String asset);
}
private interface KeyForAssetAndPackageName {
String get(String asset, String packageName);
}
private static final class FlutterState {
private final Context applicationContext;
private final BinaryMessenger binaryMessenger;
private final KeyForAssetFn keyForAsset;
private final KeyForAssetAndPackageName keyForAssetAndPackageName;
private final TextureRegistry textureRegistry;
private final MethodChannel methodChannel;
FlutterState(
Context applicationContext,
BinaryMessenger messenger,
KeyForAssetFn keyForAsset,
KeyForAssetAndPackageName keyForAssetAndPackageName,
TextureRegistry textureRegistry) {
this.applicationContext = applicationContext;
this.binaryMessenger = messenger;
this.keyForAsset = keyForAsset;
this.keyForAssetAndPackageName = keyForAssetAndPackageName;
this.textureRegistry = textureRegistry;
methodChannel = new MethodChannel(messenger, "flutter.io/videoPlayer");
}
void startListening(VideoPlayerPlugin methodCallHandler) {
methodChannel.setMethodCallHandler(methodCallHandler);
}
void stopListening() {
methodChannel.setMethodCallHandler(null);
}
}
}