Revert "Revert "[video_player] Set audio mix options (#2922)" (#2938)" (#2939)

This reverts commit d4750ea86657bf8b35f96c8f3d9bcd5a025d189b.
diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index e3f49e7..1fdfad6 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.10.12
+
+* Introduce VideoPlayerOptions to set the audio mix mode.
+
 ## 0.10.11+2
 
 * Fix aspectRatio calculation when size.width or size.height are zero.
diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
index 7bba51b..003ca18 100644
--- a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
+++ b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java
@@ -223,6 +223,31 @@
     }
   }
 
+  /** Generated class from Pigeon that represents data sent in messages. */
+  public static class MixWithOthersMessage {
+    private Boolean mixWithOthers;
+
+    public Boolean getMixWithOthers() {
+      return mixWithOthers;
+    }
+
+    public void setMixWithOthers(Boolean setterArg) {
+      this.mixWithOthers = setterArg;
+    }
+
+    HashMap toMap() {
+      HashMap<String, Object> toMapResult = new HashMap<String, Object>();
+      toMapResult.put("mixWithOthers", mixWithOthers);
+      return toMapResult;
+    }
+
+    static MixWithOthersMessage fromMap(HashMap map) {
+      MixWithOthersMessage fromMapResult = new MixWithOthersMessage();
+      fromMapResult.mixWithOthers = (Boolean) map.get("mixWithOthers");
+      return fromMapResult;
+    }
+  }
+
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   public interface VideoPlayerApi {
     void initialize();
@@ -243,6 +268,8 @@
 
     void pause(TextureMessage arg);
 
+    void setMixWithOthers(MixWithOthersMessage arg);
+
     /** Sets up an instance of `VideoPlayerApi` to handle messages through the `binaryMessenger` */
     public static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) {
       {
@@ -469,6 +496,31 @@
           channel.setMessageHandler(null);
         }
       }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<Object>(
+                binaryMessenger,
+                "dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers",
+                new StandardMessageCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              new BasicMessageChannel.MessageHandler<Object>() {
+                public void onMessage(Object message, BasicMessageChannel.Reply<Object> reply) {
+                  MixWithOthersMessage input = MixWithOthersMessage.fromMap((HashMap) message);
+                  HashMap<String, HashMap> wrapped = new HashMap<String, HashMap>();
+                  try {
+                    api.setMixWithOthers(input);
+                    wrapped.put("result", null);
+                  } catch (Exception exception) {
+                    wrapped.put("error", wrapError(exception));
+                  }
+                  reply.reply(wrapped);
+                }
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
     }
   }
 
diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
index 9db281d..801c2ca 100644
--- a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
+++ b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java
@@ -56,14 +56,18 @@
 
   private boolean isInitialized = false;
 
+  private final VideoPlayerOptions options;
+
   VideoPlayer(
       Context context,
       EventChannel eventChannel,
       TextureRegistry.SurfaceTextureEntry textureEntry,
       String dataSource,
-      String formatHint) {
+      String formatHint,
+      VideoPlayerOptions options) {
     this.eventChannel = eventChannel;
     this.textureEntry = textureEntry;
+    this.options = options;
 
     TrackSelector trackSelector = new DefaultTrackSelector();
     exoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
@@ -163,7 +167,7 @@
 
     surface = new Surface(textureEntry.surfaceTexture());
     exoPlayer.setVideoSurface(surface);
-    setAudioAttributes(exoPlayer);
+    setAudioAttributes(exoPlayer, options.mixWithOthers);
 
     exoPlayer.addListener(
         new EventListener() {
@@ -203,10 +207,10 @@
   }
 
   @SuppressWarnings("deprecation")
-  private static void setAudioAttributes(SimpleExoPlayer exoPlayer) {
+  private static void setAudioAttributes(SimpleExoPlayer exoPlayer, boolean isMixMode) {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
       exoPlayer.setAudioAttributes(
-          new AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MOVIE).build());
+          new AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MOVIE).build(), !isMixMode);
     } else {
       exoPlayer.setAudioStreamType(C.STREAM_TYPE_MUSIC);
     }
diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java
new file mode 100644
index 0000000..7381f4a
--- /dev/null
+++ b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerOptions.java
@@ -0,0 +1,5 @@
+package io.flutter.plugins.videoplayer;
+
+class VideoPlayerOptions {
+  public boolean mixWithOthers;
+}
diff --git a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java
index 3ec40e5..a22a4f2 100644
--- a/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java
+++ b/packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java
@@ -13,6 +13,7 @@
 import io.flutter.plugin.common.PluginRegistry.Registrar;
 import io.flutter.plugins.videoplayer.Messages.CreateMessage;
 import io.flutter.plugins.videoplayer.Messages.LoopingMessage;
+import io.flutter.plugins.videoplayer.Messages.MixWithOthersMessage;
 import io.flutter.plugins.videoplayer.Messages.PositionMessage;
 import io.flutter.plugins.videoplayer.Messages.TextureMessage;
 import io.flutter.plugins.videoplayer.Messages.VideoPlayerApi;
@@ -25,6 +26,7 @@
   private static final String TAG = "VideoPlayerPlugin";
   private final LongSparseArray<VideoPlayer> videoPlayers = new LongSparseArray<>();
   private FlutterState flutterState;
+  private VideoPlayerOptions options = new VideoPlayerOptions();
 
   /** Register this with the v2 embedding for the plugin to respond to lifecycle callbacks. */
   public VideoPlayerPlugin() {}
@@ -113,7 +115,8 @@
               eventChannel,
               handle,
               "asset:///" + assetLookupKey,
-              null);
+              null,
+              options);
       videoPlayers.put(handle.id(), player);
     } else {
       player =
@@ -122,7 +125,8 @@
               eventChannel,
               handle,
               arg.getUri(),
-              arg.getFormatHint());
+              arg.getFormatHint(),
+              options);
       videoPlayers.put(handle.id(), player);
     }
 
@@ -170,6 +174,11 @@
     player.pause();
   }
 
+  @Override
+  public void setMixWithOthers(MixWithOthersMessage arg) {
+    options.mixWithOthers = arg.getMixWithOthers();
+  }
+
   private interface KeyForAssetFn {
     String get(String asset);
   }
diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart
index bfe81b9..ee2fcbd 100644
--- a/packages/video_player/video_player/example/lib/main.dart
+++ b/packages/video_player/video_player/example/lib/main.dart
@@ -220,6 +220,7 @@
     _controller = VideoPlayerController.network(
       'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
       closedCaptionFile: _loadCaptions(),
+      videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
     );
 
     _controller.addListener(() {
diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
index 7dbc1b0..a834fe3 100644
--- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
+++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
@@ -560,4 +560,15 @@
   [player pause];
 }
 
+- (void)setMixWithOthers:(FLTMixWithOthersMessage*)input
+                   error:(FlutterError* _Nullable __autoreleasing*)error {
+  if ([input.mixWithOthers boolValue]) {
+    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
+                                     withOptions:AVAudioSessionCategoryOptionMixWithOthers
+                                           error:nil];
+  } else {
+    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
+  }
+}
+
 @end
diff --git a/packages/video_player/video_player/ios/Classes/messages.h b/packages/video_player/video_player/ios/Classes/messages.h
index 3c89b1f..27025ef 100644
--- a/packages/video_player/video_player/ios/Classes/messages.h
+++ b/packages/video_player/video_player/ios/Classes/messages.h
@@ -12,6 +12,7 @@
 @class FLTLoopingMessage;
 @class FLTVolumeMessage;
 @class FLTPositionMessage;
+@class FLTMixWithOthersMessage;
 
 @interface FLTTextureMessage : NSObject
 @property(nonatomic, strong, nullable) NSNumber *textureId;
@@ -39,6 +40,10 @@
 @property(nonatomic, strong, nullable) NSNumber *position;
 @end
 
+@interface FLTMixWithOthersMessage : NSObject
+@property(nonatomic, strong, nullable) NSNumber *mixWithOthers;
+@end
+
 @protocol FLTVideoPlayerApi
 - (void)initialize:(FlutterError *_Nullable *_Nonnull)error;
 - (nullable FLTTextureMessage *)create:(FLTCreateMessage *)input
@@ -51,6 +56,8 @@
                                     error:(FlutterError *_Nullable *_Nonnull)error;
 - (void)seekTo:(FLTPositionMessage *)input error:(FlutterError *_Nullable *_Nonnull)error;
 - (void)pause:(FLTTextureMessage *)input error:(FlutterError *_Nullable *_Nonnull)error;
+- (void)setMixWithOthers:(FLTMixWithOthersMessage *)input
+                   error:(FlutterError *_Nullable *_Nonnull)error;
 @end
 
 extern void FLTVideoPlayerApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
diff --git a/packages/video_player/video_player/ios/Classes/messages.m b/packages/video_player/video_player/ios/Classes/messages.m
index 3694a11..64fcd75 100644
--- a/packages/video_player/video_player/ios/Classes/messages.m
+++ b/packages/video_player/video_player/ios/Classes/messages.m
@@ -40,6 +40,10 @@
 + (FLTPositionMessage *)fromMap:(NSDictionary *)dict;
 - (NSDictionary *)toMap;
 @end
+@interface FLTMixWithOthersMessage ()
++ (FLTMixWithOthersMessage *)fromMap:(NSDictionary *)dict;
+- (NSDictionary *)toMap;
+@end
 
 @implementation FLTTextureMessage
 + (FLTTextureMessage *)fromMap:(NSDictionary *)dict {
@@ -154,6 +158,22 @@
 }
 @end
 
+@implementation FLTMixWithOthersMessage
++ (FLTMixWithOthersMessage *)fromMap:(NSDictionary *)dict {
+  FLTMixWithOthersMessage *result = [[FLTMixWithOthersMessage alloc] init];
+  result.mixWithOthers = dict[@"mixWithOthers"];
+  if ((NSNull *)result.mixWithOthers == [NSNull null]) {
+    result.mixWithOthers = nil;
+  }
+  return result;
+}
+- (NSDictionary *)toMap {
+  return [NSDictionary
+      dictionaryWithObjectsAndKeys:(self.mixWithOthers != nil ? self.mixWithOthers : [NSNull null]),
+                                   @"mixWithOthers", nil];
+}
+@end
+
 void FLTVideoPlayerApiSetup(id<FlutterBinaryMessenger> binaryMessenger, id<FLTVideoPlayerApi> api) {
   {
     FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
@@ -289,4 +309,19 @@
       [channel setMessageHandler:nil];
     }
   }
+  {
+    FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+        messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers"
+               binaryMessenger:binaryMessenger];
+    if (api) {
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        FlutterError *error;
+        FLTMixWithOthersMessage *input = [FLTMixWithOthersMessage fromMap:message];
+        [api setMixWithOthers:input error:&error];
+        callback(wrapResult(nil, error));
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
 }
diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart
index a2290b2..8cd0561 100644
--- a/packages/video_player/video_player/lib/video_player.dart
+++ b/packages/video_player/video_player/lib/video_player.dart
@@ -12,7 +12,7 @@
 
 import 'package:video_player_platform_interface/video_player_platform_interface.dart';
 export 'package:video_player_platform_interface/video_player_platform_interface.dart'
-    show DurationRange, DataSourceType, VideoFormat;
+    show DurationRange, DataSourceType, VideoFormat, VideoPlayerOptions;
 
 import 'src/closed_caption_file.dart';
 export 'src/closed_caption_file.dart';
@@ -168,7 +168,7 @@
   /// null. The [package] argument must be non-null when the asset comes from a
   /// package and null otherwise.
   VideoPlayerController.asset(this.dataSource,
-      {this.package, this.closedCaptionFile})
+      {this.package, this.closedCaptionFile, this.videoPlayerOptions})
       : dataSourceType = DataSourceType.asset,
         formatHint = null,
         super(VideoPlayerValue(duration: null));
@@ -181,7 +181,7 @@
   /// **Android only**: The [formatHint] option allows the caller to override
   /// the video format detection code.
   VideoPlayerController.network(this.dataSource,
-      {this.formatHint, this.closedCaptionFile})
+      {this.formatHint, this.closedCaptionFile, this.videoPlayerOptions})
       : dataSourceType = DataSourceType.network,
         package = null,
         super(VideoPlayerValue(duration: null));
@@ -190,7 +190,8 @@
   ///
   /// This will load the file from the file-URI given by:
   /// `'file://${file.path}'`.
-  VideoPlayerController.file(File file, {this.closedCaptionFile})
+  VideoPlayerController.file(File file,
+      {this.closedCaptionFile, this.videoPlayerOptions})
       : dataSource = 'file://${file.path}',
         dataSourceType = DataSourceType.file,
         package = null,
@@ -211,6 +212,9 @@
   /// is constructed with.
   final DataSourceType dataSourceType;
 
+  /// Provide additional configuration options (optional). Like setting the audio mode to mix
+  final VideoPlayerOptions videoPlayerOptions;
+
   /// Only set for [asset] videos. The package that the asset was loaded from.
   final String package;
 
@@ -262,6 +266,12 @@
         );
         break;
     }
+
+    if (videoPlayerOptions?.mixWithOthers != null) {
+      await _videoPlayerPlatform
+          .setMixWithOthers(videoPlayerOptions.mixWithOthers);
+    }
+
     _textureId = await _videoPlayerPlatform.create(dataSourceDescription);
     _creatingCompleter.complete(null);
     final Completer<void> initializingCompleter = Completer<void>();
diff --git a/packages/video_player/video_player/pigeons/messages.dart b/packages/video_player/video_player/pigeons/messages.dart
index 2df5b78..074eef0 100644
--- a/packages/video_player/video_player/pigeons/messages.dart
+++ b/packages/video_player/video_player/pigeons/messages.dart
@@ -26,6 +26,10 @@
   String formatHint;
 }
 
+class MixWithOthersMessage {
+  bool mixWithOthers;
+}
+
 @HostApi()
 abstract class VideoPlayerApi {
   void initialize();
@@ -37,6 +41,7 @@
   PositionMessage position(TextureMessage msg);
   void seekTo(PositionMessage msg);
   void pause(TextureMessage msg);
+  void setMixWithOthers(MixWithOthersMessage msg);
 }
 
 void configurePigeon(PigeonOptions opts) {
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index 03f71bf..a62f6f0 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -4,7 +4,7 @@
 # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump
 # the version to 2.0.0.
 # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
-version: 0.10.11+2
+version: 0.10.12
 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
 
 flutter:
diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart
index ae236de..6772259 100644
--- a/packages/video_player/video_player/test/video_player_test.dart
+++ b/packages/video_player/video_player/test/video_player_test.dart
@@ -10,8 +10,8 @@
 import 'package:flutter/widgets.dart';
 import 'package:video_player/video_player.dart';
 import 'package:flutter_test/flutter_test.dart';
-import 'package:video_player_platform_interface/video_player_platform_interface.dart';
 import 'package:video_player_platform_interface/messages.dart';
+import 'package:video_player_platform_interface/video_player_platform_interface.dart';
 
 class FakeController extends ValueNotifier<VideoPlayerValue>
     implements VideoPlayerController {
@@ -52,6 +52,9 @@
 
   @override
   Future<ClosedCaptionFile> get closedCaptionFile => _loadClosedCaption();
+
+  @override
+  VideoPlayerOptions get videoPlayerOptions => null;
 }
 
 Future<ClosedCaptionFile> _loadClosedCaption() async =>
@@ -575,6 +578,14 @@
     expect(colors.bufferedColor, bufferedColor);
     expect(colors.backgroundColor, backgroundColor);
   });
+
+  test('setMixWithOthers', () {
+    final VideoPlayerController controller = VideoPlayerController.file(
+        File(''),
+        videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true));
+    controller.initialize();
+    expect(controller.videoPlayerOptions.mixWithOthers, true);
+  });
 }
 
 class FakeVideoPlayerPlatform extends VideoPlayerApiTest {