refactor(tvos): cleanup
This commit is contained in:
+4
-4
@@ -743,8 +743,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: b4006c601e32a6a1fac9e6f26e6ee9923c7b03c9
|
ref: edfe336
|
||||||
resolved-ref: b4006c601e32a6a1fac9e6f26e6ee9923c7b03c9
|
resolved-ref: edfe33636bc82c0649c808117651b5c6844cce80
|
||||||
url: "https://github.com/edde746/os-media-controls"
|
url: "https://github.com/edde746/os-media-controls"
|
||||||
source: git
|
source: git
|
||||||
version: "0.2.1"
|
version: "0.2.1"
|
||||||
@@ -1287,10 +1287,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: universal_gamepad
|
name: universal_gamepad
|
||||||
sha256: "6177944707639a20dd4f89e7f8d78bf66b40ba03d70a1962754d3bba45e14ccd"
|
sha256: "3ada9b26e3b3471adc414fd50b8a27d2f890301d9d4cfd7a46732988a9f143d0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.4"
|
version: "1.5.5"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ dependencies:
|
|||||||
os_media_controls:
|
os_media_controls:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/edde746/os-media-controls
|
url: https://github.com/edde746/os-media-controls
|
||||||
ref: b4006c601e32a6a1fac9e6f26e6ee9923c7b03c9
|
ref: edfe336
|
||||||
rate_limiter: ^1.0.0
|
rate_limiter: ^1.0.0
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
git:
|
git:
|
||||||
|
|||||||
@@ -31,3 +31,5 @@ Runner/GeneratedPluginRegistrant.*
|
|||||||
!default.mode2v3
|
!default.mode2v3
|
||||||
!default.pbxuser
|
!default.pbxuser
|
||||||
!default.perspectivev3
|
!default.perspectivev3
|
||||||
|
# Flutter tool doesn't generate tvOS Flutter.podspec, so it's hand-maintained.
|
||||||
|
!Flutter/Flutter.podspec
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#
|
||||||
|
# This podspec is NOT to be published. It is only used as a local source!
|
||||||
|
# This is a generated file; do not edit or check into version control.
|
||||||
|
#
|
||||||
|
|
||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'Flutter'
|
||||||
|
s.version = '1.0.0'
|
||||||
|
s.summary = 'A UI toolkit for beautiful and fast apps.'
|
||||||
|
s.homepage = 'https://flutter.dev'
|
||||||
|
s.license = { :type => 'BSD' }
|
||||||
|
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
|
||||||
|
s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
|
||||||
|
s.ios.deployment_target = '13.0'
|
||||||
|
s.tvos.deployment_target = '14.0'
|
||||||
|
# Framework linking is handled by Flutter tooling, not CocoaPods.
|
||||||
|
# Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs.
|
||||||
|
s.vendored_frameworks = 'path/to/nothing'
|
||||||
|
end
|
||||||
+55
-1
@@ -7,6 +7,60 @@ project 'Runner', {
|
|||||||
'Release' => :release,
|
'Release' => :release,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Flutter's tool doesn't generate Podfile logic for tvOS. Hand-wire pods for
|
||||||
|
# plugins whose upstream podspecs already declare tvOS — the rest remain
|
||||||
|
# compiled from tvos/Runner/Plugins/ via tvos/scripts/wire_plugins.rb.
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
tvos_pod_plugins = %w[universal_gamepad os_media_controls]
|
||||||
|
|
||||||
|
plugins_deps = File.expand_path('../.flutter-plugins-dependencies', __dir__)
|
||||||
|
unless File.exist?(plugins_deps)
|
||||||
|
raise 'Run `flutter pub get` in the project root before `pod install` on tvOS'
|
||||||
|
end
|
||||||
|
ios_plugins = JSON.parse(File.read(plugins_deps)).dig('plugins', 'ios') || []
|
||||||
|
plugin_paths = ios_plugins.each_with_object({}) { |p, h| h[p['name']] = p['path'] }
|
||||||
|
|
||||||
|
# Resolve the local tvOS engine root from tvos/Flutter/Generated.xcconfig so
|
||||||
|
# pod targets can find Flutter.framework at compile time.
|
||||||
|
def tvos_flutter_engine_path
|
||||||
|
xcconfig = File.expand_path('Flutter/Generated.xcconfig', __dir__)
|
||||||
|
raise "tvos/Podfile: missing #{xcconfig}" unless File.exist?(xcconfig)
|
||||||
|
File.foreach(xcconfig) do |line|
|
||||||
|
m = line.match(/^FLUTTER_LOCAL_ENGINE=(.+)/)
|
||||||
|
return m[1].strip if m
|
||||||
|
end
|
||||||
|
raise "tvos/Podfile: FLUTTER_LOCAL_ENGINE missing from Flutter/Generated.xcconfig"
|
||||||
|
end
|
||||||
|
|
||||||
target 'Runner' do
|
target 'Runner' do
|
||||||
# No pods.
|
use_frameworks!
|
||||||
|
pod 'Flutter', :path => 'Flutter'
|
||||||
|
tvos_pod_plugins.each do |name|
|
||||||
|
base = plugin_paths[name] or
|
||||||
|
raise "tvos/Podfile: plugin '#{name}' not in .flutter-plugins-dependencies"
|
||||||
|
pod name, :path => File.join(base, 'ios')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
engine = tvos_flutter_engine_path
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
target.build_configurations.each do |config|
|
||||||
|
is_debug = config.name.downcase.include?('debug')
|
||||||
|
sim_variant = is_debug ? 'tvos_debug_sim_unopt_arm64' : 'tvos_release'
|
||||||
|
dev_variant = is_debug ? 'tvos_debug_unopt' : 'tvos_release'
|
||||||
|
config.build_settings['FRAMEWORK_SEARCH_PATHS[sdk=appletvsimulator*]'] = [
|
||||||
|
'$(inherited)',
|
||||||
|
File.join(engine, 'out', sim_variant),
|
||||||
|
]
|
||||||
|
config.build_settings['FRAMEWORK_SEARCH_PATHS[sdk=appletvos*]'] = [
|
||||||
|
'$(inherited)',
|
||||||
|
File.join(engine, 'out', dev_variant),
|
||||||
|
]
|
||||||
|
# Engine Flutter.framework ships arm64-only for simulator; exclude x86_64
|
||||||
|
# so the link step doesn't look for a slice that doesn't exist.
|
||||||
|
config.build_settings['EXCLUDED_ARCHS[sdk=appletvsimulator*]'] = 'x86_64'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,32 +7,25 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
0D69E2DEA6EA0538722BC7F0 /* ConnectivityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D0E84BD711F2744BA0D136 /* ConnectivityProvider.swift */; };
|
0A4E3554C5AD1373495F5434 /* messages.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8E3486BF50440024D81B1E /* messages.g.swift */; };
|
||||||
12E72ED3EFA35A8715DE2A52 /* GamepadStreamHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82813FF8CCC8320A8C32FC7 /* GamepadStreamHandler.swift */; };
|
24753D6DD9098E97989137A6 /* PackageInfoPlusPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C51CAD792E5C69AA4F971A /* PackageInfoPlusPlugin.swift */; };
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
31477C4FBA7BF70CAFCDC566 /* DeviceInfoPlusPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98ED539F08FAE8E8B964C7BE /* DeviceInfoPlusPlugin.swift */; };
|
||||||
30E568153D56A59EA5EADCBB /* PathProviderPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87984F20624EC6CA45E20790 /* PathProviderPlugin.swift */; };
|
|
||||||
32F0056AF154FC4EA76CBEFC /* GamepadPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD57BBEE1B72584C9C7D44B9 /* GamepadPlugin.swift */; };
|
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
477FB0F520224870902B9FD7 /* messages.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FF71CD2416844A1FC485D77 /* messages.g.swift */; };
|
4E9365FA67E94D8A289FE862 /* PathMonitorConnectivityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA12E2C6063C5799C38CAA95 /* PathMonitorConnectivityProvider.swift */; };
|
||||||
4A6DE7BDEF20A6242A3C6365 /* GCControllerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F2071908D08983E6A6A1C1E /* GCControllerManager.swift */; };
|
|
||||||
5689B077C6EA57D53BBE8D0D /* OsMediaControlsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8663A3A20A0D6F446B6B37 /* OsMediaControlsPlugin.swift */; };
|
|
||||||
5C71F5F7B33075F2B007825B /* MpvPlayerPluginShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73645904F226A24585A092CE /* MpvPlayerPluginShared.swift */; };
|
5C71F5F7B33075F2B007825B /* MpvPlayerPluginShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73645904F226A24585A092CE /* MpvPlayerPluginShared.swift */; };
|
||||||
5CDBEB7E3FE968FC131E964A /* ConnectivityPlusPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C667C1287EFBDF4DF45C5313 /* ConnectivityPlusPlugin.swift */; };
|
|
||||||
691577F0EB3F4EB1A5160280 /* MpvPlayerCoreBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A3BDA46E79969EA1DF3AC /* MpvPlayerCoreBase.swift */; };
|
691577F0EB3F4EB1A5160280 /* MpvPlayerCoreBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A3BDA46E79969EA1DF3AC /* MpvPlayerCoreBase.swift */; };
|
||||||
6A0510802F9B91CB0090B5FC /* MPVKit in Frameworks */ = {isa = PBXBuildFile; productRef = EA0F4263E7B912702C490108 /* MPVKit */; };
|
|
||||||
6F3C0DD6F2F8DA14E7E2F386 /* MpvPlayerCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12B8610AE5D580077264851 /* MpvPlayerCore.swift */; };
|
6F3C0DD6F2F8DA14E7E2F386 /* MpvPlayerCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12B8610AE5D580077264851 /* MpvPlayerCore.swift */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
8E5EED3DDAC9455D4DAA9776 /* MpvPlayerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D7A998830EDC8F77BF521D1 /* MpvPlayerPlugin.swift */; };
|
8E5EED3DDAC9455D4DAA9776 /* MpvPlayerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D7A998830EDC8F77BF521D1 /* MpvPlayerPlugin.swift */; };
|
||||||
|
937279397427A0E8ECC70A3A /* PathProviderPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217BA48DC884CB7ADD43CF88 /* PathProviderPlugin.swift */; };
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
A7ED1D92E7B15D3F7562F93E /* SharedPreferencesPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = E09D436FC3E6CD994DC1B488 /* SharedPreferencesPlugin.swift */; };
|
|
||||||
AA34E3E5872B3792D6959EAA /* MpvPipController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2635E12EB9322B151EE5127 /* MpvPipController.swift */; };
|
AA34E3E5872B3792D6959EAA /* MpvPipController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2635E12EB9322B151EE5127 /* MpvPipController.swift */; };
|
||||||
B494441CBB8FB05D04F55DD9 /* DeviceInfoPlusPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4BADB53B8D59E031563AA9 /* DeviceInfoPlusPlugin.swift */; };
|
AFCEB532D567638EF6707333 /* ConnectivityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9575190150A93F97270FC0 /* ConnectivityProvider.swift */; };
|
||||||
BF599A1C25382688DD7F4505 /* ButtonMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FBF4BBA5D82316059225B32 /* ButtonMapping.swift */; };
|
C6A15611158B29B0FF43A960 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 824D2C68D1F1206932E118C2 /* Pods_Runner.framework */; };
|
||||||
C1E0D7252DE079EDFA234F90 /* PathMonitorConnectivityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4716B74C5238614844BCA224 /* PathMonitorConnectivityProvider.swift */; };
|
C94CC73C8037BAA385B7ED5C /* SharedPreferencesPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FB5275C65B7F4CC7DD45DF /* SharedPreferencesPlugin.swift */; };
|
||||||
C330A1087AA135CCFC0C6203 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F435CB4590A55958615A7AD /* libPods-Runner.a */; };
|
F85DEBB1878182EEE1DC542A /* ConnectivityPlusPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 936A41610C7269D52E5E51FF /* ConnectivityPlusPlugin.swift */; };
|
||||||
F36688C4E8DF44BC6BDD99A5 /* PackageInfoPlusPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 300783F8CE83D2530D2EDD97 /* PackageInfoPlusPlugin.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -51,24 +44,19 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
04DD35536DEE7C27FFA53862 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
04DD35536DEE7C27FFA53862 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
0C25F4F2367A30B47E945AD6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
0C25F4F2367A30B47E945AD6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
0FF71CD2416844A1FC485D77 /* messages.g.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = messages.g.swift; sourceTree = "<group>"; };
|
0C8E3486BF50440024D81B1E /* messages.g.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = messages.g.swift; sourceTree = "<group>"; };
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
217BA48DC884CB7ADD43CF88 /* PathProviderPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PathProviderPlugin.swift; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
35FB5275C65B7F4CC7DD45DF /* SharedPreferencesPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SharedPreferencesPlugin.swift; sourceTree = "<group>"; };
|
||||||
20D0E84BD711F2744BA0D136 /* ConnectivityProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConnectivityProvider.swift; sourceTree = "<group>"; };
|
|
||||||
300783F8CE83D2530D2EDD97 /* PackageInfoPlusPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PackageInfoPlusPlugin.swift; sourceTree = "<group>"; };
|
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
3C9575190150A93F97270FC0 /* ConnectivityProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConnectivityProvider.swift; sourceTree = "<group>"; };
|
||||||
420881FB6A648A2AFD39FFF2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
420881FB6A648A2AFD39FFF2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
4716B74C5238614844BCA224 /* PathMonitorConnectivityProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PathMonitorConnectivityProvider.swift; sourceTree = "<group>"; };
|
|
||||||
5C4BADB53B8D59E031563AA9 /* DeviceInfoPlusPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DeviceInfoPlusPlugin.swift; sourceTree = "<group>"; };
|
|
||||||
5F435CB4590A55958615A7AD /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
5FBF4BBA5D82316059225B32 /* ButtonMapping.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ButtonMapping.swift; sourceTree = "<group>"; };
|
|
||||||
73645904F226A24585A092CE /* MpvPlayerPluginShared.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPlayerPluginShared.swift; path = ../shared/apple/MpvPlayer/MpvPlayerPluginShared.swift; sourceTree = "<source_root>"; };
|
73645904F226A24585A092CE /* MpvPlayerPluginShared.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPlayerPluginShared.swift; path = ../shared/apple/MpvPlayer/MpvPlayerPluginShared.swift; sourceTree = "<source_root>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
7B8663A3A20A0D6F446B6B37 /* OsMediaControlsPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = OsMediaControlsPlugin.swift; sourceTree = "<group>"; };
|
824D2C68D1F1206932E118C2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
87984F20624EC6CA45E20790 /* PathProviderPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PathProviderPlugin.swift; sourceTree = "<group>"; };
|
83C51CAD792E5C69AA4F971A /* PackageInfoPlusPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PackageInfoPlusPlugin.swift; sourceTree = "<group>"; };
|
||||||
8F2071908D08983E6A6A1C1E /* GCControllerManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GCControllerManager.swift; sourceTree = "<group>"; };
|
936A41610C7269D52E5E51FF /* ConnectivityPlusPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConnectivityPlusPlugin.swift; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -76,14 +64,12 @@
|
|||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
98ED539F08FAE8E8B964C7BE /* DeviceInfoPlusPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DeviceInfoPlusPlugin.swift; sourceTree = "<group>"; };
|
||||||
9D7A998830EDC8F77BF521D1 /* MpvPlayerPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPlayerPlugin.swift; path = ../ios/Runner/MpvPlayer/MpvPlayerPlugin.swift; sourceTree = "<source_root>"; };
|
9D7A998830EDC8F77BF521D1 /* MpvPlayerPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPlayerPlugin.swift; path = ../ios/Runner/MpvPlayer/MpvPlayerPlugin.swift; sourceTree = "<source_root>"; };
|
||||||
A12B8610AE5D580077264851 /* MpvPlayerCore.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPlayerCore.swift; path = ../ios/Runner/MpvPlayer/MpvPlayerCore.swift; sourceTree = "<source_root>"; };
|
A12B8610AE5D580077264851 /* MpvPlayerCore.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPlayerCore.swift; path = ../ios/Runner/MpvPlayer/MpvPlayerCore.swift; sourceTree = "<source_root>"; };
|
||||||
A2635E12EB9322B151EE5127 /* MpvPipController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPipController.swift; path = ../ios/Runner/MpvPlayer/MpvPipController.swift; sourceTree = "<source_root>"; };
|
A2635E12EB9322B151EE5127 /* MpvPipController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPipController.swift; path = ../ios/Runner/MpvPlayer/MpvPipController.swift; sourceTree = "<source_root>"; };
|
||||||
C667C1287EFBDF4DF45C5313 /* ConnectivityPlusPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConnectivityPlusPlugin.swift; sourceTree = "<group>"; };
|
AA12E2C6063C5799C38CAA95 /* PathMonitorConnectivityProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PathMonitorConnectivityProvider.swift; sourceTree = "<group>"; };
|
||||||
D52A3BDA46E79969EA1DF3AC /* MpvPlayerCoreBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPlayerCoreBase.swift; path = ../shared/apple/MpvPlayer/MpvPlayerCoreBase.swift; sourceTree = "<source_root>"; };
|
D52A3BDA46E79969EA1DF3AC /* MpvPlayerCoreBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MpvPlayerCoreBase.swift; path = ../shared/apple/MpvPlayer/MpvPlayerCoreBase.swift; sourceTree = "<source_root>"; };
|
||||||
D82813FF8CCC8320A8C32FC7 /* GamepadStreamHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GamepadStreamHandler.swift; sourceTree = "<group>"; };
|
|
||||||
DD57BBEE1B72584C9C7D44B9 /* GamepadPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GamepadPlugin.swift; sourceTree = "<group>"; };
|
|
||||||
E09D436FC3E6CD994DC1B488 /* SharedPreferencesPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SharedPreferencesPlugin.swift; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -91,38 +77,33 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
6A0510802F9B91CB0090B5FC /* MPVKit in Frameworks */,
|
C6A15611158B29B0FF43A960 /* Pods_Runner.framework in Frameworks */,
|
||||||
C330A1087AA135CCFC0C6203 /* libPods-Runner.a in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
0524C4A636652321237B0884 /* path_provider */ = {
|
0BB116AB9B63B9251A577795 /* connectivity_plus */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
87984F20624EC6CA45E20790 /* PathProviderPlugin.swift */,
|
936A41610C7269D52E5E51FF /* ConnectivityPlusPlugin.swift */,
|
||||||
|
3C9575190150A93F97270FC0 /* ConnectivityProvider.swift */,
|
||||||
|
AA12E2C6063C5799C38CAA95 /* PathMonitorConnectivityProvider.swift */,
|
||||||
);
|
);
|
||||||
path = path_provider;
|
name = connectivity_plus;
|
||||||
|
path = connectivity_plus;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
08F5845B7875697970629696 /* package_info_plus */ = {
|
5167AAFF7134262A07EBBC7C /* package_info_plus */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
300783F8CE83D2530D2EDD97 /* PackageInfoPlusPlugin.swift */,
|
83C51CAD792E5C69AA4F971A /* PackageInfoPlusPlugin.swift */,
|
||||||
);
|
);
|
||||||
|
name = package_info_plus;
|
||||||
path = package_info_plus;
|
path = package_info_plus;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
19DB3FBCF738894832DA07EF /* device_info_plus */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
5C4BADB53B8D59E031563AA9 /* DeviceInfoPlusPlugin.swift */,
|
|
||||||
);
|
|
||||||
path = device_info_plus;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
53CDD29681161166962B9FA5 /* Pods */ = {
|
53CDD29681161166962B9FA5 /* Pods */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -133,31 +114,34 @@
|
|||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
5815557FAD15751414018354 /* shared_preferences_foundation */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E09D436FC3E6CD994DC1B488 /* SharedPreferencesPlugin.swift */,
|
|
||||||
0FF71CD2416844A1FC485D77 /* messages.g.swift */,
|
|
||||||
);
|
|
||||||
path = shared_preferences_foundation;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
5833EC5B503BBB4E370BA1B7 /* Frameworks */ = {
|
5833EC5B503BBB4E370BA1B7 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5F435CB4590A55958615A7AD /* libPods-Runner.a */,
|
824D2C68D1F1206932E118C2 /* Pods_Runner.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
737C2C621E266B83D4011221 /* connectivity_plus */ = {
|
6C18DC1E07D29FCDF63F3159 /* Plugins */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C667C1287EFBDF4DF45C5313 /* ConnectivityPlusPlugin.swift */,
|
ACB880260B35BB030208DF38 /* shared_preferences_foundation */,
|
||||||
20D0E84BD711F2744BA0D136 /* ConnectivityProvider.swift */,
|
5167AAFF7134262A07EBBC7C /* package_info_plus */,
|
||||||
4716B74C5238614844BCA224 /* PathMonitorConnectivityProvider.swift */,
|
9DF350B3C8D3FFF46CF8FEF7 /* path_provider */,
|
||||||
|
7CEBB3A4438717E61CFCBA1E /* device_info_plus */,
|
||||||
|
0BB116AB9B63B9251A577795 /* connectivity_plus */,
|
||||||
);
|
);
|
||||||
path = connectivity_plus;
|
name = Plugins;
|
||||||
|
path = Plugins;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
7CEBB3A4438717E61CFCBA1E /* device_info_plus */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
98ED539F08FAE8E8B964C7BE /* DeviceInfoPlusPlugin.swift */,
|
||||||
|
);
|
||||||
|
name = device_info_plus;
|
||||||
|
path = device_info_plus;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
@@ -197,25 +181,31 @@
|
|||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
97C147021CF9000F007C117D /* Info.plist */,
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||||
BF0B7DFE378943E9D7865D18 /* MpvPlayer */,
|
BF0B7DFE378943E9D7865D18 /* MpvPlayer */,
|
||||||
E6BAF734A5201F83A1B61DCC /* Plugins */,
|
6C18DC1E07D29FCDF63F3159 /* Plugins */,
|
||||||
);
|
);
|
||||||
path = Runner;
|
path = Runner;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
AD5C822C9001042A47B99FBD /* universal_gamepad */ = {
|
9DF350B3C8D3FFF46CF8FEF7 /* path_provider */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DD57BBEE1B72584C9C7D44B9 /* GamepadPlugin.swift */,
|
217BA48DC884CB7ADD43CF88 /* PathProviderPlugin.swift */,
|
||||||
D82813FF8CCC8320A8C32FC7 /* GamepadStreamHandler.swift */,
|
|
||||||
8F2071908D08983E6A6A1C1E /* GCControllerManager.swift */,
|
|
||||||
5FBF4BBA5D82316059225B32 /* ButtonMapping.swift */,
|
|
||||||
);
|
);
|
||||||
path = universal_gamepad;
|
name = path_provider;
|
||||||
|
path = path_provider;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
ACB880260B35BB030208DF38 /* shared_preferences_foundation */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
35FB5275C65B7F4CC7DD45DF /* SharedPreferencesPlugin.swift */,
|
||||||
|
0C8E3486BF50440024D81B1E /* messages.g.swift */,
|
||||||
|
);
|
||||||
|
name = shared_preferences_foundation;
|
||||||
|
path = shared_preferences_foundation;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF0B7DFE378943E9D7865D18 /* MpvPlayer */ = {
|
BF0B7DFE378943E9D7865D18 /* MpvPlayer */ = {
|
||||||
@@ -231,28 +221,6 @@
|
|||||||
path = Runner/MpvPlayer;
|
path = Runner/MpvPlayer;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
C538704E1F003452B22FE0C7 /* os_media_controls */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
7B8663A3A20A0D6F446B6B37 /* OsMediaControlsPlugin.swift */,
|
|
||||||
);
|
|
||||||
path = os_media_controls;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E6BAF734A5201F83A1B61DCC /* Plugins */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
5815557FAD15751414018354 /* shared_preferences_foundation */,
|
|
||||||
08F5845B7875697970629696 /* package_info_plus */,
|
|
||||||
0524C4A636652321237B0884 /* path_provider */,
|
|
||||||
AD5C822C9001042A47B99FBD /* universal_gamepad */,
|
|
||||||
19DB3FBCF738894832DA07EF /* device_info_plus */,
|
|
||||||
737C2C621E266B83D4011221 /* connectivity_plus */,
|
|
||||||
C538704E1F003452B22FE0C7 /* os_media_controls */,
|
|
||||||
);
|
|
||||||
path = Plugins;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -267,6 +235,7 @@
|
|||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
7C33FC680D67B3859BF9AE6C /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -367,6 +336,23 @@
|
|||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
|
7C33FC680D67B3859BF9AE6C /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -389,25 +375,19 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
|
||||||
691577F0EB3F4EB1A5160280 /* MpvPlayerCoreBase.swift in Sources */,
|
691577F0EB3F4EB1A5160280 /* MpvPlayerCoreBase.swift in Sources */,
|
||||||
5C71F5F7B33075F2B007825B /* MpvPlayerPluginShared.swift in Sources */,
|
5C71F5F7B33075F2B007825B /* MpvPlayerPluginShared.swift in Sources */,
|
||||||
6F3C0DD6F2F8DA14E7E2F386 /* MpvPlayerCore.swift in Sources */,
|
6F3C0DD6F2F8DA14E7E2F386 /* MpvPlayerCore.swift in Sources */,
|
||||||
8E5EED3DDAC9455D4DAA9776 /* MpvPlayerPlugin.swift in Sources */,
|
8E5EED3DDAC9455D4DAA9776 /* MpvPlayerPlugin.swift in Sources */,
|
||||||
AA34E3E5872B3792D6959EAA /* MpvPipController.swift in Sources */,
|
AA34E3E5872B3792D6959EAA /* MpvPipController.swift in Sources */,
|
||||||
A7ED1D92E7B15D3F7562F93E /* SharedPreferencesPlugin.swift in Sources */,
|
C94CC73C8037BAA385B7ED5C /* SharedPreferencesPlugin.swift in Sources */,
|
||||||
477FB0F520224870902B9FD7 /* messages.g.swift in Sources */,
|
0A4E3554C5AD1373495F5434 /* messages.g.swift in Sources */,
|
||||||
F36688C4E8DF44BC6BDD99A5 /* PackageInfoPlusPlugin.swift in Sources */,
|
24753D6DD9098E97989137A6 /* PackageInfoPlusPlugin.swift in Sources */,
|
||||||
30E568153D56A59EA5EADCBB /* PathProviderPlugin.swift in Sources */,
|
937279397427A0E8ECC70A3A /* PathProviderPlugin.swift in Sources */,
|
||||||
32F0056AF154FC4EA76CBEFC /* GamepadPlugin.swift in Sources */,
|
31477C4FBA7BF70CAFCDC566 /* DeviceInfoPlusPlugin.swift in Sources */,
|
||||||
12E72ED3EFA35A8715DE2A52 /* GamepadStreamHandler.swift in Sources */,
|
F85DEBB1878182EEE1DC542A /* ConnectivityPlusPlugin.swift in Sources */,
|
||||||
4A6DE7BDEF20A6242A3C6365 /* GCControllerManager.swift in Sources */,
|
AFCEB532D567638EF6707333 /* ConnectivityProvider.swift in Sources */,
|
||||||
BF599A1C25382688DD7F4505 /* ButtonMapping.swift in Sources */,
|
4E9365FA67E94D8A289FE862 /* PathMonitorConnectivityProvider.swift in Sources */,
|
||||||
B494441CBB8FB05D04F55DD9 /* DeviceInfoPlusPlugin.swift in Sources */,
|
|
||||||
5CDBEB7E3FE968FC131E964A /* ConnectivityPlusPlugin.swift in Sources */,
|
|
||||||
0D69E2DEA6EA0538722BC7F0 /* ConnectivityProvider.swift in Sources */,
|
|
||||||
C1E0D7252DE079EDFA234F90 /* PathMonitorConnectivityProvider.swift in Sources */,
|
|
||||||
5689B077C6EA57D53BBE8D0D /* OsMediaControlsPlugin.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import Flutter
|
import Flutter
|
||||||
import UIKit
|
import UIKit
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import universal_gamepad
|
||||||
|
import os_media_controls
|
||||||
|
|
||||||
@main
|
@main
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
@@ -18,47 +20,28 @@ import AVFoundation
|
|||||||
|
|
||||||
application.beginReceivingRemoteControlEvents()
|
application.beginReceivingRemoteControlEvents()
|
||||||
|
|
||||||
NSLog("[tvos] AppDelegate didFinishLaunching — registering plugins")
|
if let r = self.registrar(forPlugin: "SharedPreferencesPlugin") {
|
||||||
|
SharedPreferencesPlugin.register(with: r)
|
||||||
if let spRegistrar = self.registrar(forPlugin: "SharedPreferencesPlugin") {
|
|
||||||
NSLog("[tvos] Registering SharedPreferencesPlugin (Swift-direct)")
|
|
||||||
SharedPreferencesPlugin.register(with: spRegistrar)
|
|
||||||
} else {
|
|
||||||
NSLog("[tvos] SharedPreferencesPlugin registrar nil")
|
|
||||||
}
|
}
|
||||||
|
if let r = self.registrar(forPlugin: "MpvPlayerPlugin") {
|
||||||
if let mpvRegistrar = self.registrar(forPlugin: "MpvPlayerPlugin") {
|
MpvPlayerPlugin.register(with: r)
|
||||||
NSLog("[tvos] Registering MpvPlayerPlugin")
|
|
||||||
MpvPlayerPlugin.register(with: mpvRegistrar)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let r = self.registrar(forPlugin: "PackageInfoPlusPlugin") {
|
if let r = self.registrar(forPlugin: "PackageInfoPlusPlugin") {
|
||||||
NSLog("[tvos] Registering PackageInfoPlusPlugin")
|
|
||||||
PackageInfoPlusPlugin.register(with: r)
|
PackageInfoPlusPlugin.register(with: r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let r = self.registrar(forPlugin: "PathProviderPlugin") {
|
if let r = self.registrar(forPlugin: "PathProviderPlugin") {
|
||||||
NSLog("[tvos] Registering PathProviderPlugin")
|
|
||||||
PathProviderPlugin.register(with: r)
|
PathProviderPlugin.register(with: r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let r = self.registrar(forPlugin: "GamepadPlugin") {
|
if let r = self.registrar(forPlugin: "GamepadPlugin") {
|
||||||
NSLog("[tvos] Registering GamepadPlugin")
|
|
||||||
GamepadPlugin.register(with: r)
|
GamepadPlugin.register(with: r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let r = self.registrar(forPlugin: "DeviceInfoPlusPlugin") {
|
if let r = self.registrar(forPlugin: "DeviceInfoPlusPlugin") {
|
||||||
NSLog("[tvos] Registering DeviceInfoPlusPlugin")
|
|
||||||
DeviceInfoPlusPlugin.register(with: r)
|
DeviceInfoPlusPlugin.register(with: r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let r = self.registrar(forPlugin: "ConnectivityPlusPlugin") {
|
if let r = self.registrar(forPlugin: "ConnectivityPlusPlugin") {
|
||||||
NSLog("[tvos] Registering ConnectivityPlusPlugin")
|
|
||||||
ConnectivityPlusPlugin.register(with: r)
|
ConnectivityPlusPlugin.register(with: r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let r = self.registrar(forPlugin: "OsMediaControlsPlugin") {
|
if let r = self.registrar(forPlugin: "OsMediaControlsPlugin") {
|
||||||
NSLog("[tvos] Registering OsMediaControlsPlugin")
|
|
||||||
OsMediaControlsPlugin.register(with: r)
|
OsMediaControlsPlugin.register(with: r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,370 +0,0 @@
|
|||||||
import Flutter
|
|
||||||
import UIKit
|
|
||||||
import MediaPlayer
|
|
||||||
import AVFoundation
|
|
||||||
|
|
||||||
public class OsMediaControlsPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
|
|
||||||
private var eventSink: FlutterEventSink?
|
|
||||||
private let nowPlayingCenter = MPNowPlayingInfoCenter.default()
|
|
||||||
private let commandCenter = MPRemoteCommandCenter.shared()
|
|
||||||
|
|
||||||
private var currentMetadata: [String: Any] = [:]
|
|
||||||
|
|
||||||
|
|
||||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
|
||||||
let methodChannel = FlutterMethodChannel(
|
|
||||||
name: "com.edde746.os_media_controls/methods",
|
|
||||||
binaryMessenger: registrar.messenger()
|
|
||||||
)
|
|
||||||
let eventChannel = FlutterEventChannel(
|
|
||||||
name: "com.edde746.os_media_controls/events",
|
|
||||||
binaryMessenger: registrar.messenger()
|
|
||||||
)
|
|
||||||
|
|
||||||
let instance = OsMediaControlsPlugin()
|
|
||||||
registrar.addMethodCallDelegate(instance, channel: methodChannel)
|
|
||||||
eventChannel.setStreamHandler(instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override init() {
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
// Ensure app receives remote control events for Now Playing controls
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
UIApplication.shared.beginReceivingRemoteControlEvents()
|
|
||||||
}
|
|
||||||
|
|
||||||
setupRemoteCommandCenter()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupRemoteCommandCenter() {
|
|
||||||
// Play command
|
|
||||||
commandCenter.playCommand.isEnabled = true
|
|
||||||
commandCenter.playCommand.addTarget { [weak self] event in
|
|
||||||
self?.sendEvent(["type": "play"])
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause command
|
|
||||||
commandCenter.pauseCommand.isEnabled = true
|
|
||||||
commandCenter.pauseCommand.addTarget { [weak self] event in
|
|
||||||
self?.sendEvent(["type": "pause"])
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle play/pause command
|
|
||||||
commandCenter.togglePlayPauseCommand.isEnabled = true
|
|
||||||
commandCenter.togglePlayPauseCommand.addTarget { [weak self] event in
|
|
||||||
self?.sendEvent(["type": "togglePlayPause"])
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next track command
|
|
||||||
commandCenter.nextTrackCommand.isEnabled = true
|
|
||||||
commandCenter.nextTrackCommand.addTarget { [weak self] event in
|
|
||||||
self?.sendEvent(["type": "next"])
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
|
|
||||||
// Previous track command
|
|
||||||
commandCenter.previousTrackCommand.isEnabled = true
|
|
||||||
commandCenter.previousTrackCommand.addTarget { [weak self] event in
|
|
||||||
self?.sendEvent(["type": "previous"])
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change playback position command (seek)
|
|
||||||
commandCenter.changePlaybackPositionCommand.isEnabled = true
|
|
||||||
commandCenter.changePlaybackPositionCommand.addTarget { [weak self] event in
|
|
||||||
if let positionEvent = event as? MPChangePlaybackPositionCommandEvent {
|
|
||||||
self?.sendEvent([
|
|
||||||
"type": "seek",
|
|
||||||
"position": positionEvent.positionTime
|
|
||||||
])
|
|
||||||
}
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip forward command
|
|
||||||
commandCenter.skipForwardCommand.isEnabled = false // Disabled by default
|
|
||||||
commandCenter.skipForwardCommand.addTarget { [weak self] event in
|
|
||||||
if let skipEvent = event as? MPSkipIntervalCommandEvent {
|
|
||||||
self?.sendEvent([
|
|
||||||
"type": "skipForward",
|
|
||||||
"interval": skipEvent.interval
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
self?.sendEvent(["type": "skipForward"])
|
|
||||||
}
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip backward command
|
|
||||||
commandCenter.skipBackwardCommand.isEnabled = false // Disabled by default
|
|
||||||
commandCenter.skipBackwardCommand.addTarget { [weak self] event in
|
|
||||||
if let skipEvent = event as? MPSkipIntervalCommandEvent {
|
|
||||||
self?.sendEvent([
|
|
||||||
"type": "skipBackward",
|
|
||||||
"interval": skipEvent.interval
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
self?.sendEvent(["type": "skipBackward"])
|
|
||||||
}
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change playback rate command
|
|
||||||
commandCenter.changePlaybackRateCommand.isEnabled = true
|
|
||||||
commandCenter.changePlaybackRateCommand.supportedPlaybackRates = [0.5, 1.0, 1.5, 2.0]
|
|
||||||
commandCenter.changePlaybackRateCommand.addTarget { [weak self] event in
|
|
||||||
if let rateEvent = event as? MPChangePlaybackRateCommandEvent {
|
|
||||||
self?.sendEvent([
|
|
||||||
"type": "setSpeed",
|
|
||||||
"speed": rateEvent.playbackRate
|
|
||||||
])
|
|
||||||
}
|
|
||||||
return .success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
||||||
switch call.method {
|
|
||||||
case "setMetadata":
|
|
||||||
setMetadata(arguments: call.arguments as? [String: Any])
|
|
||||||
result(nil)
|
|
||||||
|
|
||||||
case "setPlaybackState":
|
|
||||||
setPlaybackState(arguments: call.arguments as? [String: Any])
|
|
||||||
result(nil)
|
|
||||||
|
|
||||||
case "enableControls":
|
|
||||||
enableControls(arguments: call.arguments as? [String])
|
|
||||||
result(nil)
|
|
||||||
|
|
||||||
case "disableControls":
|
|
||||||
disableControls(arguments: call.arguments as? [String])
|
|
||||||
result(nil)
|
|
||||||
|
|
||||||
case "setSkipIntervals":
|
|
||||||
setSkipIntervals(arguments: call.arguments as? [String: Any])
|
|
||||||
result(nil)
|
|
||||||
|
|
||||||
case "setQueueInfo":
|
|
||||||
setQueueInfo(arguments: call.arguments as? [String: Any])
|
|
||||||
result(nil)
|
|
||||||
|
|
||||||
case "clear":
|
|
||||||
clear()
|
|
||||||
result(nil)
|
|
||||||
|
|
||||||
default:
|
|
||||||
result(FlutterMethodNotImplemented)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setMetadata(arguments: [String: Any]?) {
|
|
||||||
guard let args = arguments else { return }
|
|
||||||
|
|
||||||
// Reactivate audio session to ensure media controls work after being cleared
|
|
||||||
do {
|
|
||||||
try AVAudioSession.sharedInstance().setActive(true)
|
|
||||||
} catch {
|
|
||||||
print("Failed to reactivate audio session: \(error)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store metadata for later use
|
|
||||||
for (key, value) in args {
|
|
||||||
if key != "artwork" {
|
|
||||||
currentMetadata[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nowPlayingInfo = nowPlayingCenter.nowPlayingInfo ?? [:]
|
|
||||||
|
|
||||||
if let title = args["title"] as? String {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyTitle] = title
|
|
||||||
}
|
|
||||||
if let artist = args["artist"] as? String {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyArtist] = artist
|
|
||||||
}
|
|
||||||
if let album = args["album"] as? String {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = album
|
|
||||||
}
|
|
||||||
if let albumArtist = args["albumArtist"] as? String {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyAlbumArtist] = albumArtist
|
|
||||||
}
|
|
||||||
if let duration = args["duration"] as? Double {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration
|
|
||||||
}
|
|
||||||
// Handle artwork from bytes (takes precedence over URL)
|
|
||||||
if let artworkData = args["artwork"] as? FlutterStandardTypedData {
|
|
||||||
if let image = UIImage(data: artworkData.data) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in image }
|
|
||||||
}
|
|
||||||
} else if let artworkUrlString = args["artworkUrl"] as? String, let artworkUrl = URL(string: artworkUrlString) {
|
|
||||||
// Handle artwork from URL - download asynchronously
|
|
||||||
URLSession.shared.dataTask(with: artworkUrl) { [weak self] data, response, error in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard error == nil, let data = data, let image = UIImage(data: data) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
var updatedInfo = self.nowPlayingCenter.nowPlayingInfo ?? [:]
|
|
||||||
updatedInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in image }
|
|
||||||
self.nowPlayingCenter.nowPlayingInfo = updatedInfo
|
|
||||||
}
|
|
||||||
}.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
nowPlayingCenter.nowPlayingInfo = nowPlayingInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setPlaybackState(arguments: [String: Any]?) {
|
|
||||||
guard let args = arguments,
|
|
||||||
let stateString = args["state"] as? String,
|
|
||||||
let position = args["position"] as? Double,
|
|
||||||
let speed = args["speed"] as? Double else { return }
|
|
||||||
|
|
||||||
// Reactivate audio session to ensure media controls work after being cleared
|
|
||||||
do {
|
|
||||||
try AVAudioSession.sharedInstance().setActive(true)
|
|
||||||
} catch {
|
|
||||||
print("Failed to reactivate audio session: \(error)")
|
|
||||||
}
|
|
||||||
|
|
||||||
var nowPlayingInfo = nowPlayingCenter.nowPlayingInfo ?? [:]
|
|
||||||
|
|
||||||
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = position
|
|
||||||
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] =
|
|
||||||
stateString == "playing" ? speed : 0.0
|
|
||||||
|
|
||||||
nowPlayingCenter.nowPlayingInfo = nowPlayingInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
private func enableControls(arguments: [String]?) {
|
|
||||||
guard let controls = arguments else { return }
|
|
||||||
|
|
||||||
for control in controls {
|
|
||||||
switch control {
|
|
||||||
case "play":
|
|
||||||
commandCenter.playCommand.isEnabled = true
|
|
||||||
case "pause":
|
|
||||||
commandCenter.pauseCommand.isEnabled = true
|
|
||||||
case "stop":
|
|
||||||
// iOS doesn't have a dedicated stop command
|
|
||||||
break
|
|
||||||
case "next":
|
|
||||||
commandCenter.nextTrackCommand.isEnabled = true
|
|
||||||
case "previous":
|
|
||||||
commandCenter.previousTrackCommand.isEnabled = true
|
|
||||||
case "seek":
|
|
||||||
commandCenter.changePlaybackPositionCommand.isEnabled = true
|
|
||||||
case "skipForward":
|
|
||||||
commandCenter.skipForwardCommand.isEnabled = true
|
|
||||||
case "skipBackward":
|
|
||||||
commandCenter.skipBackwardCommand.isEnabled = true
|
|
||||||
case "changeSpeed":
|
|
||||||
commandCenter.changePlaybackRateCommand.isEnabled = true
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func disableControls(arguments: [String]?) {
|
|
||||||
guard let controls = arguments else { return }
|
|
||||||
|
|
||||||
for control in controls {
|
|
||||||
switch control {
|
|
||||||
case "play":
|
|
||||||
commandCenter.playCommand.isEnabled = false
|
|
||||||
case "pause":
|
|
||||||
commandCenter.pauseCommand.isEnabled = false
|
|
||||||
case "next":
|
|
||||||
commandCenter.nextTrackCommand.isEnabled = false
|
|
||||||
case "previous":
|
|
||||||
commandCenter.previousTrackCommand.isEnabled = false
|
|
||||||
case "seek":
|
|
||||||
commandCenter.changePlaybackPositionCommand.isEnabled = false
|
|
||||||
case "skipForward":
|
|
||||||
commandCenter.skipForwardCommand.isEnabled = false
|
|
||||||
case "skipBackward":
|
|
||||||
commandCenter.skipBackwardCommand.isEnabled = false
|
|
||||||
case "changeSpeed":
|
|
||||||
commandCenter.changePlaybackRateCommand.isEnabled = false
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setSkipIntervals(arguments: [String: Any]?) {
|
|
||||||
guard let args = arguments else { return }
|
|
||||||
|
|
||||||
if let forward = args["forward"] as? Int {
|
|
||||||
commandCenter.skipForwardCommand.isEnabled = true
|
|
||||||
commandCenter.skipForwardCommand.preferredIntervals = [NSNumber(value: forward)]
|
|
||||||
}
|
|
||||||
|
|
||||||
if let backward = args["backward"] as? Int {
|
|
||||||
commandCenter.skipBackwardCommand.isEnabled = true
|
|
||||||
commandCenter.skipBackwardCommand.preferredIntervals = [NSNumber(value: backward)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setQueueInfo(arguments: [String: Any]?) {
|
|
||||||
guard let args = arguments,
|
|
||||||
let currentIndex = args["currentIndex"] as? Int,
|
|
||||||
let queueLength = args["queueLength"] as? Int else { return }
|
|
||||||
|
|
||||||
var nowPlayingInfo = nowPlayingCenter.nowPlayingInfo ?? [:]
|
|
||||||
|
|
||||||
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackQueueIndex] = currentIndex
|
|
||||||
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackQueueCount] = queueLength
|
|
||||||
|
|
||||||
nowPlayingCenter.nowPlayingInfo = nowPlayingInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
private func clear() {
|
|
||||||
nowPlayingCenter.nowPlayingInfo = nil
|
|
||||||
currentMetadata.removeAll()
|
|
||||||
|
|
||||||
// Deactivate audio session to force iOS to remove controls from Control Center
|
|
||||||
do {
|
|
||||||
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
|
||||||
} catch {
|
|
||||||
// Audio session deactivation failed, but continue with cleanup
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable all command center buttons
|
|
||||||
let commandCenter = MPRemoteCommandCenter.shared()
|
|
||||||
commandCenter.playCommand.isEnabled = false
|
|
||||||
commandCenter.pauseCommand.isEnabled = false
|
|
||||||
commandCenter.togglePlayPauseCommand.isEnabled = false
|
|
||||||
commandCenter.nextTrackCommand.isEnabled = false
|
|
||||||
commandCenter.previousTrackCommand.isEnabled = false
|
|
||||||
commandCenter.changePlaybackPositionCommand.isEnabled = false
|
|
||||||
commandCenter.skipForwardCommand.isEnabled = false
|
|
||||||
commandCenter.skipBackwardCommand.isEnabled = false
|
|
||||||
commandCenter.changePlaybackRateCommand.isEnabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private func sendEvent(_ event: [String: Any]) {
|
|
||||||
eventSink?(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - FlutterStreamHandler
|
|
||||||
|
|
||||||
public func onListen(withArguments arguments: Any?,
|
|
||||||
eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
|
||||||
self.eventSink = events
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
|
||||||
self.eventSink = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
import GameController
|
|
||||||
|
|
||||||
/// Maps GCController elements to W3C Standard Gamepad indices.
|
|
||||||
///
|
|
||||||
/// W3C Standard Gamepad button mapping:
|
|
||||||
/// 0 = A (South) 8 = Back/View
|
|
||||||
/// 1 = B (East) 9 = Start/Menu
|
|
||||||
/// 2 = X (West) 10 = Left Stick Button
|
|
||||||
/// 3 = Y (North) 11 = Right Stick Button
|
|
||||||
/// 4 = Left Shoulder 12 = D-pad Up
|
|
||||||
/// 5 = Right Shoulder 13 = D-pad Down
|
|
||||||
/// 6 = Left Trigger 14 = D-pad Left
|
|
||||||
/// 7 = Right Trigger 15 = D-pad Right
|
|
||||||
/// 16 = Guide/Home
|
|
||||||
///
|
|
||||||
/// W3C Standard Gamepad axis mapping:
|
|
||||||
/// 0 = Left Stick X (-1 left, +1 right)
|
|
||||||
/// 1 = Left Stick Y (-1 up, +1 down)
|
|
||||||
/// 2 = Right Stick X (-1 left, +1 right)
|
|
||||||
/// 3 = Right Stick Y (-1 up, +1 down)
|
|
||||||
///
|
|
||||||
/// Note: GCController reports Y-axis with up = +1, which is the opposite of
|
|
||||||
/// the W3C convention (up = -1). The axis mapping inverts Y values.
|
|
||||||
enum ButtonMapping {
|
|
||||||
|
|
||||||
/// Returns the W3C button index and the corresponding `GCControllerButtonInput`
|
|
||||||
/// if `element` matches a known button in `gamepad`.
|
|
||||||
static func buttonIndex(for element: GCControllerElement,
|
|
||||||
in gamepad: GCExtendedGamepad) -> (Int, GCControllerButtonInput)? {
|
|
||||||
// Face buttons
|
|
||||||
if element == gamepad.buttonA {
|
|
||||||
return (0, gamepad.buttonA)
|
|
||||||
}
|
|
||||||
if element == gamepad.buttonB {
|
|
||||||
return (1, gamepad.buttonB)
|
|
||||||
}
|
|
||||||
if element == gamepad.buttonX {
|
|
||||||
return (2, gamepad.buttonX)
|
|
||||||
}
|
|
||||||
if element == gamepad.buttonY {
|
|
||||||
return (3, gamepad.buttonY)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shoulders
|
|
||||||
if element == gamepad.leftShoulder {
|
|
||||||
return (4, gamepad.leftShoulder)
|
|
||||||
}
|
|
||||||
if element == gamepad.rightShoulder {
|
|
||||||
return (5, gamepad.rightShoulder)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Triggers
|
|
||||||
if element == gamepad.leftTrigger {
|
|
||||||
return (6, gamepad.leftTrigger)
|
|
||||||
}
|
|
||||||
if element == gamepad.rightTrigger {
|
|
||||||
return (7, gamepad.rightTrigger)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Menu buttons
|
|
||||||
if let buttonOptions = gamepad.buttonOptions, element == buttonOptions {
|
|
||||||
return (8, buttonOptions)
|
|
||||||
}
|
|
||||||
if element == gamepad.buttonMenu {
|
|
||||||
return (9, gamepad.buttonMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thumbstick buttons
|
|
||||||
if let leftThumbstickButton = gamepad.leftThumbstickButton, element == leftThumbstickButton {
|
|
||||||
return (10, leftThumbstickButton)
|
|
||||||
}
|
|
||||||
if let rightThumbstickButton = gamepad.rightThumbstickButton, element == rightThumbstickButton {
|
|
||||||
return (11, rightThumbstickButton)
|
|
||||||
}
|
|
||||||
|
|
||||||
// D-pad individual buttons
|
|
||||||
if element == gamepad.dpad.up {
|
|
||||||
return (12, gamepad.dpad.up)
|
|
||||||
}
|
|
||||||
if element == gamepad.dpad.down {
|
|
||||||
return (13, gamepad.dpad.down)
|
|
||||||
}
|
|
||||||
if element == gamepad.dpad.left {
|
|
||||||
return (14, gamepad.dpad.left)
|
|
||||||
}
|
|
||||||
if element == gamepad.dpad.right {
|
|
||||||
return (15, gamepad.dpad.right)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guide / Home button (added in tvOS/iOS 14).
|
|
||||||
if #available(iOS 14.0, tvOS 14.0, macOS 11.0, *) {
|
|
||||||
if let buttonHome = gamepad.buttonHome, element == buttonHome {
|
|
||||||
return (16, buttonHome)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all W3C axis events for the given `element`.
|
|
||||||
///
|
|
||||||
/// The `valueChangedHandler` on `GCExtendedGamepad` may fire with the
|
|
||||||
/// entire `GCControllerDirectionPad` (thumbstick) as the element, or with
|
|
||||||
/// an individual axis sub-element. When the full thumbstick fires we
|
|
||||||
/// return both X and Y axis entries. When a single axis fires we return
|
|
||||||
/// one entry.
|
|
||||||
///
|
|
||||||
/// Y-axis values are inverted: GCController uses +1 for up, but the W3C
|
|
||||||
/// standard uses -1 for up.
|
|
||||||
static func axisIndices(for element: GCControllerElement,
|
|
||||||
in gamepad: GCExtendedGamepad) -> [(Int, Double)] {
|
|
||||||
// Left stick (full thumbstick element)
|
|
||||||
if element == gamepad.leftThumbstick {
|
|
||||||
return [
|
|
||||||
(0, Double(gamepad.leftThumbstick.xAxis.value)),
|
|
||||||
(1, Double(-gamepad.leftThumbstick.yAxis.value)),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left stick individual axes
|
|
||||||
if element == gamepad.leftThumbstick.xAxis {
|
|
||||||
return [(0, Double(gamepad.leftThumbstick.xAxis.value))]
|
|
||||||
}
|
|
||||||
if element == gamepad.leftThumbstick.yAxis {
|
|
||||||
// Invert Y: GC up is +1, W3C up is -1
|
|
||||||
return [(1, Double(-gamepad.leftThumbstick.yAxis.value))]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right stick (full thumbstick element)
|
|
||||||
if element == gamepad.rightThumbstick {
|
|
||||||
return [
|
|
||||||
(2, Double(gamepad.rightThumbstick.xAxis.value)),
|
|
||||||
(3, Double(-gamepad.rightThumbstick.yAxis.value)),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right stick individual axes
|
|
||||||
if element == gamepad.rightThumbstick.xAxis {
|
|
||||||
return [(2, Double(gamepad.rightThumbstick.xAxis.value))]
|
|
||||||
}
|
|
||||||
if element == gamepad.rightThumbstick.yAxis {
|
|
||||||
// Invert Y: GC up is +1, W3C up is -1
|
|
||||||
return [(3, Double(-gamepad.rightThumbstick.yAxis.value))]
|
|
||||||
}
|
|
||||||
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
import GameController
|
|
||||||
|
|
||||||
/// Manages GCController connections and input, translating events into the
|
|
||||||
/// compact wire-format arrays expected by the Dart side.
|
|
||||||
class GCControllerManager {
|
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
private let streamHandler: GamepadStreamHandler
|
|
||||||
|
|
||||||
/// Maps a GCController instance to its stable int gamepad ID.
|
|
||||||
private var controllerIds: [ObjectIdentifier: Int] = [:]
|
|
||||||
|
|
||||||
/// Counter for generating unique IDs.
|
|
||||||
private var nextId: Int = 0
|
|
||||||
|
|
||||||
/// Notification observers so we can remove them on dispose.
|
|
||||||
private var connectObserver: NSObjectProtocol?
|
|
||||||
private var disconnectObserver: NSObjectProtocol?
|
|
||||||
|
|
||||||
// MARK: - Init
|
|
||||||
|
|
||||||
init(streamHandler: GamepadStreamHandler) {
|
|
||||||
self.streamHandler = streamHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
|
||||||
|
|
||||||
/// Begin listening for GCController connect/disconnect notifications and
|
|
||||||
/// register input handlers on any controllers that are already connected.
|
|
||||||
func startObserving() {
|
|
||||||
connectObserver = NotificationCenter.default.addObserver(
|
|
||||||
forName: .GCControllerDidConnect,
|
|
||||||
object: nil,
|
|
||||||
queue: .main
|
|
||||||
) { [weak self] notification in
|
|
||||||
guard let self = self,
|
|
||||||
let controller = notification.object as? GCController else { return }
|
|
||||||
self.controllerDidConnect(controller)
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectObserver = NotificationCenter.default.addObserver(
|
|
||||||
forName: .GCControllerDidDisconnect,
|
|
||||||
object: nil,
|
|
||||||
queue: .main
|
|
||||||
) { [weak self] notification in
|
|
||||||
guard let self = self,
|
|
||||||
let controller = notification.object as? GCController else { return }
|
|
||||||
self.controllerDidDisconnect(controller)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle controllers that were already connected before we started
|
|
||||||
// observing (e.g. wired controllers).
|
|
||||||
for controller in GCController.controllers() {
|
|
||||||
controllerDidConnect(controller)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove all observers and clear input handlers.
|
|
||||||
func stopObserving() {
|
|
||||||
if let observer = connectObserver {
|
|
||||||
NotificationCenter.default.removeObserver(observer)
|
|
||||||
connectObserver = nil
|
|
||||||
}
|
|
||||||
if let observer = disconnectObserver {
|
|
||||||
NotificationCenter.default.removeObserver(observer)
|
|
||||||
disconnectObserver = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove value changed handlers from all tracked controllers.
|
|
||||||
for controller in GCController.controllers() {
|
|
||||||
controller.extendedGamepad?.valueChangedHandler = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
controllerIds.removeAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - listGamepads
|
|
||||||
|
|
||||||
/// Returns an array of dictionaries describing currently connected
|
|
||||||
/// controllers, suitable for returning over a MethodChannel.
|
|
||||||
func listGamepads() -> [[String: Any]] {
|
|
||||||
var result: [[String: Any]] = []
|
|
||||||
for controller in GCController.controllers() {
|
|
||||||
let key = ObjectIdentifier(controller)
|
|
||||||
guard let gamepadId = controllerIds[key] else { continue }
|
|
||||||
|
|
||||||
var info: [String: Any] = [
|
|
||||||
"id": gamepadId,
|
|
||||||
"name": controllerName(controller),
|
|
||||||
]
|
|
||||||
|
|
||||||
if let vendorId = vendorId(for: controller) {
|
|
||||||
info["vendorId"] = vendorId
|
|
||||||
}
|
|
||||||
if let productId = productId(for: controller) {
|
|
||||||
info["productId"] = productId
|
|
||||||
}
|
|
||||||
|
|
||||||
result.append(info)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Connection events
|
|
||||||
|
|
||||||
private func controllerDidConnect(_ controller: GCController) {
|
|
||||||
let gamepadId = assignId(for: controller)
|
|
||||||
|
|
||||||
// Register input handlers for the extended gamepad profile.
|
|
||||||
registerInputHandlers(for: controller, gamepadId: gamepadId)
|
|
||||||
|
|
||||||
// Wire format: [0, gamepadId, timestamp, connected, name, vendorId, productId]
|
|
||||||
let event: [Any] = [
|
|
||||||
0,
|
|
||||||
gamepadId,
|
|
||||||
currentTimestamp(),
|
|
||||||
true,
|
|
||||||
controllerName(controller),
|
|
||||||
vendorId(for: controller) as Any,
|
|
||||||
productId(for: controller) as Any,
|
|
||||||
]
|
|
||||||
streamHandler.send(event: event)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func controllerDidDisconnect(_ controller: GCController) {
|
|
||||||
let key = ObjectIdentifier(controller)
|
|
||||||
guard let gamepadId = controllerIds.removeValue(forKey: key) else { return }
|
|
||||||
|
|
||||||
controller.extendedGamepad?.valueChangedHandler = nil
|
|
||||||
|
|
||||||
// Wire format: [0, gamepadId, timestamp, connected, name, vendorId, productId]
|
|
||||||
let event: [Any] = [
|
|
||||||
0,
|
|
||||||
gamepadId,
|
|
||||||
currentTimestamp(),
|
|
||||||
false,
|
|
||||||
controllerName(controller),
|
|
||||||
vendorId(for: controller) as Any,
|
|
||||||
productId(for: controller) as Any,
|
|
||||||
]
|
|
||||||
streamHandler.send(event: event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Input handlers
|
|
||||||
|
|
||||||
private func registerInputHandlers(for controller: GCController, gamepadId: Int) {
|
|
||||||
guard let extendedGamepad = controller.extendedGamepad else { return }
|
|
||||||
|
|
||||||
extendedGamepad.valueChangedHandler = { [weak self] (gamepad, element) in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.handleValueChanged(gamepad: gamepad, element: element, gamepadId: gamepadId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleValueChanged(gamepad: GCExtendedGamepad,
|
|
||||||
element: GCControllerElement,
|
|
||||||
gamepadId: Int) {
|
|
||||||
let timestamp = currentTimestamp()
|
|
||||||
|
|
||||||
// D-pad fires as a single GCControllerDirectionPad element, not as
|
|
||||||
// individual direction buttons. Expand it into 4 button events.
|
|
||||||
if element === gamepad.dpad {
|
|
||||||
sendButtonEvent(gamepadId: gamepadId, index: 12,
|
|
||||||
pressed: gamepad.dpad.up.isPressed,
|
|
||||||
value: Double(gamepad.dpad.up.value), timestamp: timestamp)
|
|
||||||
sendButtonEvent(gamepadId: gamepadId, index: 13,
|
|
||||||
pressed: gamepad.dpad.down.isPressed,
|
|
||||||
value: Double(gamepad.dpad.down.value), timestamp: timestamp)
|
|
||||||
sendButtonEvent(gamepadId: gamepadId, index: 14,
|
|
||||||
pressed: gamepad.dpad.left.isPressed,
|
|
||||||
value: Double(gamepad.dpad.left.value), timestamp: timestamp)
|
|
||||||
sendButtonEvent(gamepadId: gamepadId, index: 15,
|
|
||||||
pressed: gamepad.dpad.right.isPressed,
|
|
||||||
value: Double(gamepad.dpad.right.value), timestamp: timestamp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check buttons.
|
|
||||||
if let (index, button) = ButtonMapping.buttonIndex(for: element, in: gamepad) {
|
|
||||||
sendButtonEvent(gamepadId: gamepadId, index: index,
|
|
||||||
pressed: button.isPressed,
|
|
||||||
value: Double(button.value), timestamp: timestamp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check axes (may produce multiple events when a full thumbstick fires).
|
|
||||||
let axisEvents = ButtonMapping.axisIndices(for: element, in: gamepad)
|
|
||||||
for (index, axisValue) in axisEvents {
|
|
||||||
// Wire format: [2, gamepadId, timestamp, axisIndex, value]
|
|
||||||
streamHandler.send(event: [2, gamepadId, timestamp, index, axisValue])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func sendButtonEvent(gamepadId: Int, index: Int,
|
|
||||||
pressed: Bool, value: Double, timestamp: Int) {
|
|
||||||
// Wire format: [1, gamepadId, timestamp, buttonIndex, pressed, value]
|
|
||||||
streamHandler.send(event: [1, gamepadId, timestamp, index, pressed, value])
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Helpers
|
|
||||||
|
|
||||||
private func assignId(for controller: GCController) -> Int {
|
|
||||||
let key = ObjectIdentifier(controller)
|
|
||||||
if let existing = controllerIds[key] {
|
|
||||||
return existing
|
|
||||||
}
|
|
||||||
let id = nextId
|
|
||||||
nextId += 1
|
|
||||||
controllerIds[key] = id
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
private func controllerName(_ controller: GCController) -> String {
|
|
||||||
let category = controller.productCategory
|
|
||||||
if !category.isEmpty {
|
|
||||||
return category
|
|
||||||
}
|
|
||||||
return controller.vendorName ?? "Unknown Controller"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the USB vendor ID for the controller, if available.
|
|
||||||
///
|
|
||||||
/// GCController does not directly expose numeric vendor/product IDs
|
|
||||||
/// through the public API. We attempt to obtain them from the
|
|
||||||
/// underlying physical device description on iOS 16+.
|
|
||||||
private func vendorId(for controller: GCController) -> Int? {
|
|
||||||
// GCController does not publicly expose numeric vendor IDs on iOS.
|
|
||||||
// Return nil; the Dart side treats this as optional.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the USB product ID for the controller, if available.
|
|
||||||
private func productId(for controller: GCController) -> Int? {
|
|
||||||
// GCController does not publicly expose numeric product IDs on iOS.
|
|
||||||
// Return nil; the Dart side treats this as optional.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current timestamp in milliseconds since epoch.
|
|
||||||
private func currentTimestamp() -> Int {
|
|
||||||
return Int(Date().timeIntervalSince1970 * 1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import Flutter
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/// Entry point for the gamepad iOS plugin.
|
|
||||||
///
|
|
||||||
/// Registers a MethodChannel (`dev.universal_gamepad/methods`) and an
|
|
||||||
/// EventChannel (`dev.universal_gamepad/events`) with the Flutter engine.
|
|
||||||
public class GamepadPlugin: NSObject, FlutterPlugin {
|
|
||||||
|
|
||||||
private let controllerManager: GCControllerManager
|
|
||||||
private let streamHandler: GamepadStreamHandler
|
|
||||||
|
|
||||||
init(controllerManager: GCControllerManager, streamHandler: GamepadStreamHandler) {
|
|
||||||
self.controllerManager = controllerManager
|
|
||||||
self.streamHandler = streamHandler
|
|
||||||
super.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - FlutterPlugin
|
|
||||||
|
|
||||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
|
||||||
let streamHandler = GamepadStreamHandler()
|
|
||||||
let controllerManager = GCControllerManager(streamHandler: streamHandler)
|
|
||||||
|
|
||||||
let methodChannel = FlutterMethodChannel(
|
|
||||||
name: "dev.universal_gamepad/methods",
|
|
||||||
binaryMessenger: registrar.messenger()
|
|
||||||
)
|
|
||||||
|
|
||||||
let eventChannel = FlutterEventChannel(
|
|
||||||
name: "dev.universal_gamepad/events",
|
|
||||||
binaryMessenger: registrar.messenger()
|
|
||||||
)
|
|
||||||
|
|
||||||
let instance = GamepadPlugin(
|
|
||||||
controllerManager: controllerManager,
|
|
||||||
streamHandler: streamHandler
|
|
||||||
)
|
|
||||||
|
|
||||||
registrar.addMethodCallDelegate(instance, channel: methodChannel)
|
|
||||||
eventChannel.setStreamHandler(streamHandler)
|
|
||||||
|
|
||||||
// Start observing controllers immediately so we capture connections
|
|
||||||
// that happen before the Dart side listens.
|
|
||||||
controllerManager.startObserving()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - MethodChannel
|
|
||||||
|
|
||||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
||||||
switch call.method {
|
|
||||||
case "listGamepads":
|
|
||||||
result(controllerManager.listGamepads())
|
|
||||||
case "dispose":
|
|
||||||
controllerManager.stopObserving()
|
|
||||||
result(nil)
|
|
||||||
default:
|
|
||||||
result(FlutterMethodNotImplemented)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
import Flutter
|
|
||||||
|
|
||||||
/// FlutterStreamHandler that forwards native gamepad events to Dart.
|
|
||||||
///
|
|
||||||
/// Events are queued if no sink is attached yet; once a listener subscribes
|
|
||||||
/// all queued events are flushed immediately.
|
|
||||||
public class GamepadStreamHandler: NSObject, FlutterStreamHandler {
|
|
||||||
|
|
||||||
/// The active event sink provided by Flutter's EventChannel.
|
|
||||||
private var eventSink: FlutterEventSink?
|
|
||||||
|
|
||||||
/// Events received before the Dart side starts listening.
|
|
||||||
private var pendingEvents: [[Any]] = []
|
|
||||||
|
|
||||||
/// Thread-safety lock for sink / queue access.
|
|
||||||
private let lock = NSLock()
|
|
||||||
|
|
||||||
// MARK: - FlutterStreamHandler
|
|
||||||
|
|
||||||
public func onListen(withArguments arguments: Any?,
|
|
||||||
eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
|
||||||
lock.lock()
|
|
||||||
eventSink = events
|
|
||||||
|
|
||||||
// Flush any events that arrived before the listener was attached.
|
|
||||||
for event in pendingEvents {
|
|
||||||
events(event)
|
|
||||||
}
|
|
||||||
pendingEvents.removeAll()
|
|
||||||
lock.unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
|
||||||
lock.lock()
|
|
||||||
eventSink = nil
|
|
||||||
lock.unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Internal API
|
|
||||||
|
|
||||||
/// Sends a gamepad event array to Dart.
|
|
||||||
///
|
|
||||||
/// If the sink is not yet available the event is queued.
|
|
||||||
func send(event: [Any]) {
|
|
||||||
lock.lock()
|
|
||||||
if let sink = eventSink {
|
|
||||||
lock.unlock()
|
|
||||||
// Dispatch on the main thread to satisfy Flutter platform channel
|
|
||||||
// requirements.
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
sink(event)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pendingEvents.append(event)
|
|
||||||
lock.unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#import "GeneratedPluginRegistrant.h"
|
|
||||||
|
|||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Runs `pod install` for the tvOS target with UTF-8 locale forced — CocoaPods
|
||||||
|
# 1.16 on Ruby 4.0 crashes with Encoding::CompatibilityError when LANG/LC_ALL
|
||||||
|
# are unset or non-UTF-8.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TVOS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$TVOS_DIR"
|
||||||
|
|
||||||
|
export LANG=en_US.UTF-8
|
||||||
|
export LC_ALL=en_US.UTF-8
|
||||||
|
|
||||||
|
exec pod install "$@"
|
||||||
@@ -16,15 +16,10 @@ PLUGIN_BASENAMES = %w[
|
|||||||
messages.g.swift
|
messages.g.swift
|
||||||
PackageInfoPlusPlugin.swift
|
PackageInfoPlusPlugin.swift
|
||||||
PathProviderPlugin.swift
|
PathProviderPlugin.swift
|
||||||
GamepadPlugin.swift
|
|
||||||
GamepadStreamHandler.swift
|
|
||||||
GCControllerManager.swift
|
|
||||||
ButtonMapping.swift
|
|
||||||
DeviceInfoPlusPlugin.swift
|
DeviceInfoPlusPlugin.swift
|
||||||
ConnectivityPlusPlugin.swift
|
ConnectivityPlusPlugin.swift
|
||||||
ConnectivityProvider.swift
|
ConnectivityProvider.swift
|
||||||
PathMonitorConnectivityProvider.swift
|
PathMonitorConnectivityProvider.swift
|
||||||
OsMediaControlsPlugin.swift
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Remove any existing file refs for these basenames from the entire project.
|
# Remove any existing file refs for these basenames from the entire project.
|
||||||
@@ -60,12 +55,6 @@ plugins = {
|
|||||||
'path_provider' => %w[
|
'path_provider' => %w[
|
||||||
PathProviderPlugin.swift
|
PathProviderPlugin.swift
|
||||||
],
|
],
|
||||||
'universal_gamepad' => %w[
|
|
||||||
GamepadPlugin.swift
|
|
||||||
GamepadStreamHandler.swift
|
|
||||||
GCControllerManager.swift
|
|
||||||
ButtonMapping.swift
|
|
||||||
],
|
|
||||||
'device_info_plus' => %w[
|
'device_info_plus' => %w[
|
||||||
DeviceInfoPlusPlugin.swift
|
DeviceInfoPlusPlugin.swift
|
||||||
],
|
],
|
||||||
@@ -74,9 +63,9 @@ plugins = {
|
|||||||
ConnectivityProvider.swift
|
ConnectivityProvider.swift
|
||||||
PathMonitorConnectivityProvider.swift
|
PathMonitorConnectivityProvider.swift
|
||||||
],
|
],
|
||||||
'os_media_controls' => %w[
|
# universal_gamepad + os_media_controls come from CocoaPods (tvos/Podfile)
|
||||||
OsMediaControlsPlugin.swift
|
# — their upstream podspecs declare tvOS support, so no embedded copy
|
||||||
],
|
# is needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
sources_phase = runner.source_build_phase
|
sources_phase = runner.source_build_phase
|
||||||
|
|||||||
Reference in New Issue
Block a user