27 changed files with 924 additions and 181 deletions
@ -0,0 +1,27 @@ |
|||||
|
import 'package:deep_voice/core/utils/logger.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import 'package:music_service/music_service.dart'; |
||||
|
|
||||
|
//音乐播放管理 |
||||
|
class MusicManager extends GetxService { |
||||
|
static MusicManager get to => Get.find<MusicManager>(); |
||||
|
|
||||
|
// 音乐服务实例 |
||||
|
final MusicService _musicService = MusicService(); |
||||
|
|
||||
|
/// 初始化 |
||||
|
@override |
||||
|
void onInit() { |
||||
|
super.onInit(); |
||||
|
Logger.info('BLE管理器初始化'); |
||||
|
} |
||||
|
|
||||
|
/// 释放资源 |
||||
|
@override |
||||
|
void onClose() { |
||||
|
super.onClose(); |
||||
|
} |
||||
|
|
||||
|
//播放 音乐 |
||||
|
Future<void> play(String url) async {} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
# Miscellaneous |
||||
|
*.class |
||||
|
*.log |
||||
|
*.pyc |
||||
|
*.swp |
||||
|
.DS_Store |
||||
|
.atom/ |
||||
|
.build/ |
||||
|
.buildlog/ |
||||
|
.history |
||||
|
.svn/ |
||||
|
.swiftpm/ |
||||
|
migrate_working_dir/ |
||||
|
|
||||
|
# IntelliJ related |
||||
|
*.iml |
||||
|
*.ipr |
||||
|
*.iws |
||||
|
.idea/ |
||||
|
|
||||
|
# The .vscode folder contains launch configuration and tasks you configure in |
||||
|
# VS Code which you may wish to be included in version control, so this line |
||||
|
# is commented out by default. |
||||
|
#.vscode/ |
||||
|
|
||||
|
# Flutter/Dart/Pub related |
||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. |
||||
|
/pubspec.lock |
||||
|
**/doc/api/ |
||||
|
.dart_tool/ |
||||
|
.flutter-plugins |
||||
|
.flutter-plugins-dependencies |
||||
|
build/ |
||||
@ -0,0 +1,33 @@ |
|||||
|
# This file tracks properties of this Flutter project. |
||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc. |
||||
|
# |
||||
|
# This file should be version controlled and should not be manually edited. |
||||
|
|
||||
|
version: |
||||
|
revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf" |
||||
|
channel: "stable" |
||||
|
|
||||
|
project_type: plugin |
||||
|
|
||||
|
# Tracks metadata for the flutter migrate command |
||||
|
migration: |
||||
|
platforms: |
||||
|
- platform: root |
||||
|
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf |
||||
|
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf |
||||
|
- platform: android |
||||
|
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf |
||||
|
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf |
||||
|
- platform: ios |
||||
|
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf |
||||
|
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf |
||||
|
|
||||
|
# User provided section |
||||
|
|
||||
|
# List of Local paths (relative to this file) that should be |
||||
|
# ignored by the migrate tool. |
||||
|
# |
||||
|
# Files that are not part of the templates will be ignored by default. |
||||
|
unmanaged_files: |
||||
|
- 'lib/main.dart' |
||||
|
- 'ios/Runner.xcodeproj/project.pbxproj' |
||||
@ -0,0 +1,3 @@ |
|||||
|
## 0.0.1 |
||||
|
|
||||
|
* TODO: Describe initial release. |
||||
@ -0,0 +1 @@ |
|||||
|
TODO: Add your license here. |
||||
@ -0,0 +1,15 @@ |
|||||
|
# music_service |
||||
|
|
||||
|
A new Flutter project. |
||||
|
|
||||
|
## Getting Started |
||||
|
|
||||
|
This project is a starting point for a Flutter |
||||
|
[plug-in package](https://flutter.dev/to/develop-plugins), |
||||
|
a specialized package that includes platform-specific implementation code for |
||||
|
Android and/or iOS. |
||||
|
|
||||
|
For help getting started with Flutter development, view the |
||||
|
[online documentation](https://docs.flutter.dev), which offers tutorials, |
||||
|
samples, guidance on mobile development, and a full API reference. |
||||
|
|
||||
@ -0,0 +1,4 @@ |
|||||
|
include: package:flutter_lints/flutter.yaml |
||||
|
|
||||
|
# Additional information about this file can be found at |
||||
|
# https://dart.dev/guides/language/analysis-options |
||||
@ -0,0 +1,9 @@ |
|||||
|
*.iml |
||||
|
.gradle |
||||
|
/local.properties |
||||
|
/.idea/workspace.xml |
||||
|
/.idea/libraries |
||||
|
.DS_Store |
||||
|
/build |
||||
|
/captures |
||||
|
.cxx |
||||
@ -0,0 +1,70 @@ |
|||||
|
group = "com.yunqiinnovation.music_service" |
||||
|
version = "1.0-SNAPSHOT" |
||||
|
|
||||
|
buildscript { |
||||
|
ext.kotlin_version = "1.8.22" |
||||
|
repositories { |
||||
|
google() |
||||
|
mavenCentral() |
||||
|
} |
||||
|
|
||||
|
dependencies { |
||||
|
classpath("com.android.tools.build:gradle:8.7.0") |
||||
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
allprojects { |
||||
|
repositories { |
||||
|
google() |
||||
|
mavenCentral() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
apply plugin: "com.android.library" |
||||
|
apply plugin: "kotlin-android" |
||||
|
|
||||
|
android { |
||||
|
namespace = "com.yunqiinnovation.music_service" |
||||
|
|
||||
|
compileSdk = 35 |
||||
|
|
||||
|
compileOptions { |
||||
|
sourceCompatibility = JavaVersion.VERSION_11 |
||||
|
targetCompatibility = JavaVersion.VERSION_11 |
||||
|
} |
||||
|
|
||||
|
kotlinOptions { |
||||
|
jvmTarget = JavaVersion.VERSION_11 |
||||
|
} |
||||
|
|
||||
|
sourceSets { |
||||
|
main.java.srcDirs += "src/main/kotlin" |
||||
|
test.java.srcDirs += "src/test/kotlin" |
||||
|
} |
||||
|
|
||||
|
defaultConfig { |
||||
|
minSdk = 21 |
||||
|
} |
||||
|
|
||||
|
dependencies { |
||||
|
testImplementation("org.jetbrains.kotlin:kotlin-test") |
||||
|
testImplementation("org.mockito:mockito-core:5.0.0") |
||||
|
} |
||||
|
|
||||
|
testOptions { |
||||
|
unitTests.all { |
||||
|
useJUnitPlatform() |
||||
|
|
||||
|
testLogging { |
||||
|
events "passed", "skipped", "failed", "standardOut", "standardError" |
||||
|
outputs.upToDateWhen {false} |
||||
|
showStandardStreams = true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
dependencies { |
||||
|
implementation project(':deepvoice_utils') |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
rootProject.name = 'music_service' |
||||
@ -0,0 +1,11 @@ |
|||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
package="com.yunqiinnovation.music_service"> |
||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> |
||||
|
<application> |
||||
|
<service |
||||
|
android:name=".MusicService" |
||||
|
android:exported="false" |
||||
|
android:enabled="true" |
||||
|
android:foregroundServiceType="mediaPlayback" /> |
||||
|
</application> |
||||
|
</manifest> |
||||
@ -0,0 +1,153 @@ |
|||||
|
package com.yunqiinnovation.music_service |
||||
|
|
||||
|
import android.app.Notification |
||||
|
import android.app.NotificationChannel |
||||
|
import android.app.NotificationManager |
||||
|
import android.app.PendingIntent |
||||
|
import android.app.Service |
||||
|
import android.content.Context |
||||
|
import android.content.Intent |
||||
|
import android.media.AudioManager |
||||
|
import android.media.MediaPlayer |
||||
|
import android.os.Build |
||||
|
import android.os.IBinder |
||||
|
import android.util.Log |
||||
|
import androidx.core.app.NotificationCompat |
||||
|
import com.yunqiinnovation.music_service.MusicServiceStarter |
||||
|
import kotlinx.coroutines.CoroutineScope |
||||
|
import java.util.concurrent.atomic.AtomicBoolean |
||||
|
|
||||
|
class MusicService : Service() { |
||||
|
|
||||
|
companion object { |
||||
|
private const val TAG = "MusicService" |
||||
|
private const val CHANNEL_ID = "music_media_service_channel" |
||||
|
private const val NOTIFICATION_ID = 1002 |
||||
|
private const val PREF_NAME = "deep_voice_config" |
||||
|
|
||||
|
// 用于跟踪服务是否正在运行 |
||||
|
private val isRunning = AtomicBoolean(false) |
||||
|
|
||||
|
// 获取服务运行状态 |
||||
|
fun isServiceRunning(): Boolean = isRunning.get() |
||||
|
} |
||||
|
private var mediaPlayer: MediaPlayer? = null |
||||
|
private val playlist = mutableListOf<String>() // 可扩展支持播放列表 |
||||
|
private var currentIndex = 0 |
||||
|
|
||||
|
override fun onCreate() { |
||||
|
super.onCreate() |
||||
|
val notification = createNotification() // 创建一个非空通知 |
||||
|
startForeground(1, notification) |
||||
|
} |
||||
|
private var isPlaying = false |
||||
|
/** |
||||
|
* 创建前台服务通知 |
||||
|
*/ |
||||
|
private fun createNotification(): Notification { |
||||
|
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager |
||||
|
|
||||
|
// Android 8.0+ 需要创建通知渠道 |
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
||||
|
val channel = NotificationChannel( |
||||
|
CHANNEL_ID, |
||||
|
"音乐播放服务", |
||||
|
NotificationManager.IMPORTANCE_LOW |
||||
|
) |
||||
|
manager.createNotificationChannel(channel) |
||||
|
} |
||||
|
|
||||
|
// 点击通知打开主界面 |
||||
|
val intent = packageManager.getLaunchIntentForPackage(packageName)?.apply { |
||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP |
||||
|
} |
||||
|
val pendingIntent = if (intent != null) { |
||||
|
PendingIntent.getActivity( |
||||
|
this, |
||||
|
0, |
||||
|
intent, |
||||
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT |
||||
|
) |
||||
|
} else { |
||||
|
null |
||||
|
} |
||||
|
|
||||
|
// 构建通知 |
||||
|
val builder = NotificationCompat.Builder(this, CHANNEL_ID) |
||||
|
.setContentTitle("DeepSound") |
||||
|
.setContentText(if (isPlaying) "正在播放" else "已暂停") |
||||
|
.setSmallIcon(android.R.drawable.ic_media_play) |
||||
|
|
||||
|
// 设置点击意图 |
||||
|
pendingIntent?.let { builder.setContentIntent(it) } |
||||
|
|
||||
|
return builder.build() |
||||
|
} |
||||
|
|
||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { |
||||
|
val command = intent?.getStringExtra("command") ?: return START_NOT_STICKY |
||||
|
Log.i(TAG, "onStartCommand: $command") |
||||
|
when (command) { |
||||
|
"play" -> { |
||||
|
val url = intent.getStringExtra("url") ?: return START_NOT_STICKY |
||||
|
play(url) |
||||
|
} |
||||
|
"pause" -> mediaPlayer?.pause() |
||||
|
"resume" -> mediaPlayer?.start() |
||||
|
"stop" -> stopSelf() |
||||
|
"next" -> { |
||||
|
currentIndex++ |
||||
|
if (currentIndex < playlist.size) play(playlist[currentIndex]) |
||||
|
} |
||||
|
"previous" -> { |
||||
|
currentIndex-- |
||||
|
if (currentIndex >= 0) play(playlist[currentIndex]) |
||||
|
} |
||||
|
"setVolume" -> { |
||||
|
val volume = intent.getFloatExtra("volume", 1.0f) |
||||
|
mediaPlayer?.setVolume(volume, volume) |
||||
|
} |
||||
|
} |
||||
|
return START_STICKY |
||||
|
} |
||||
|
|
||||
|
private fun play(url: String) { |
||||
|
Log.i(TAG, "开始播放音乐: $url") |
||||
|
mediaPlayer?.release() |
||||
|
mediaPlayer = MediaPlayer().apply { |
||||
|
setDataSource(url) |
||||
|
setAudioStreamType(AudioManager.STREAM_MUSIC) |
||||
|
prepareAsync() |
||||
|
setOnPreparedListener { start() } |
||||
|
setOnCompletionListener { stopSelf() } |
||||
|
} |
||||
|
// startForeground(1, NotificationUtils.createNotification(this, "Playing...")) |
||||
|
} |
||||
|
|
||||
|
override fun onDestroy() { |
||||
|
mediaPlayer?.release() |
||||
|
stopForeground(true) |
||||
|
super.onDestroy() |
||||
|
} |
||||
|
|
||||
|
override fun onBind(intent: Intent?): IBinder? = null |
||||
|
|
||||
|
/** |
||||
|
* 释放资源 |
||||
|
*/ |
||||
|
fun release() { |
||||
|
try { |
||||
|
mediaPlayer?.apply { |
||||
|
if (isPlaying) stop() |
||||
|
release() |
||||
|
} |
||||
|
mediaPlayer = null |
||||
|
} catch (e: Exception) { |
||||
|
// 忽略异常 |
||||
|
} |
||||
|
} |
||||
|
/** |
||||
|
* 是否正在播放 |
||||
|
*/ |
||||
|
fun isPlaying() = mediaPlayer?.isPlaying == true |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
package com.yunqiinnovation.music_service |
||||
|
|
||||
|
import android.content.Context |
||||
|
import android.content.Intent |
||||
|
import androidx.core.content.ContextCompat |
||||
|
import com.yunqiinnovation.deepvoice_utils.FileLogger |
||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin |
||||
|
import io.flutter.plugin.common.MethodCall |
||||
|
import io.flutter.plugin.common.MethodChannel |
||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler |
||||
|
|
||||
|
|
||||
|
/** MusicServicePlugin */ |
||||
|
class MusicServicePlugin: FlutterPlugin, MethodCallHandler { |
||||
|
private val TAG = "MusicServicePlugin" |
||||
|
private lateinit var context: Context |
||||
|
private lateinit var channel : MethodChannel |
||||
|
|
||||
|
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { |
||||
|
context = flutterPluginBinding.applicationContext |
||||
|
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "music_service") |
||||
|
channel.setMethodCallHandler(this) |
||||
|
} |
||||
|
|
||||
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { |
||||
|
FileLogger.d(TAG, "Method call: ${call.method}") |
||||
|
val intent = Intent(context, MusicService::class.java) |
||||
|
when (call.method) { |
||||
|
"play" -> { |
||||
|
val url = call.argument<String>("url") |
||||
|
MusicServiceStarter.startServiceWithCommand(context, "play", url) |
||||
|
} |
||||
|
"pause" -> MusicServiceStarter.startServiceWithCommand(context, "pause") |
||||
|
"resume" -> MusicServiceStarter.startServiceWithCommand(context, "resume") |
||||
|
"stop" -> MusicServiceStarter.startServiceWithCommand(context, "stop") |
||||
|
"setVolume" -> { |
||||
|
val volume = call.argument<Double>("volume")?.toFloat() |
||||
|
MusicServiceStarter.startServiceWithCommand(context, "setVolume", volume = volume) |
||||
|
} |
||||
|
else -> result.notImplemented() |
||||
|
} |
||||
|
result.success(null) |
||||
|
} |
||||
|
|
||||
|
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.yunqiinnovation.music_service |
||||
|
|
||||
|
import android.content.Context |
||||
|
import android.content.Intent |
||||
|
import android.util.Log |
||||
|
import androidx.core.content.ContextCompat |
||||
|
|
||||
|
object MusicServiceStarter { |
||||
|
private val TAG = "MusicServiceStarter" |
||||
|
fun startServiceWithCommand(context: Context, command: String, url: String? = null, volume: Float? = null) { |
||||
|
Log.i(TAG, "startServiceWithCommand: $command $url") |
||||
|
val intent = Intent(context, MusicService::class.java).apply { |
||||
|
putExtra("command", command) |
||||
|
url?.let { putExtra("url", it) } |
||||
|
volume?.let { putExtra("volume", it) } |
||||
|
} |
||||
|
ContextCompat.startForegroundService(context, intent) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
.idea/ |
||||
|
.vagrant/ |
||||
|
.sconsign.dblite |
||||
|
.svn/ |
||||
|
|
||||
|
.DS_Store |
||||
|
*.swp |
||||
|
profile |
||||
|
|
||||
|
DerivedData/ |
||||
|
build/ |
||||
|
GeneratedPluginRegistrant.h |
||||
|
GeneratedPluginRegistrant.m |
||||
|
|
||||
|
.generated/ |
||||
|
|
||||
|
*.pbxuser |
||||
|
*.mode1v3 |
||||
|
*.mode2v3 |
||||
|
*.perspectivev3 |
||||
|
|
||||
|
!default.pbxuser |
||||
|
!default.mode1v3 |
||||
|
!default.mode2v3 |
||||
|
!default.perspectivev3 |
||||
|
|
||||
|
xcuserdata |
||||
|
|
||||
|
*.moved-aside |
||||
|
|
||||
|
*.pyc |
||||
|
*sync/ |
||||
|
Icon? |
||||
|
.tags* |
||||
|
|
||||
|
/Flutter/Generated.xcconfig |
||||
|
/Flutter/ephemeral/ |
||||
|
/Flutter/flutter_export_environment.sh |
||||
@ -0,0 +1,19 @@ |
|||||
|
import Flutter |
||||
|
import UIKit |
||||
|
|
||||
|
public class MusicServicePlugin: NSObject, FlutterPlugin { |
||||
|
public static func register(with registrar: FlutterPluginRegistrar) { |
||||
|
let channel = FlutterMethodChannel(name: "music_service", binaryMessenger: registrar.messenger()) |
||||
|
let instance = MusicServicePlugin() |
||||
|
registrar.addMethodCallDelegate(instance, channel: channel) |
||||
|
} |
||||
|
|
||||
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
||||
|
switch call.method { |
||||
|
case "getPlatformVersion": |
||||
|
result("iOS " + UIDevice.current.systemVersion) |
||||
|
default: |
||||
|
result(FlutterMethodNotImplemented) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
|
<plist version="1.0"> |
||||
|
<dict> |
||||
|
<key>NSPrivacyTrackingDomains</key> |
||||
|
<array/> |
||||
|
<key>NSPrivacyAccessedAPITypes</key> |
||||
|
<array/> |
||||
|
<key>NSPrivacyCollectedDataTypes</key> |
||||
|
<array/> |
||||
|
<key>NSPrivacyTracking</key> |
||||
|
<false/> |
||||
|
</dict> |
||||
|
</plist> |
||||
@ -0,0 +1,29 @@ |
|||||
|
# |
||||
|
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. |
||||
|
# Run `pod lib lint music_service.podspec` to validate before publishing. |
||||
|
# |
||||
|
Pod::Spec.new do |s| |
||||
|
s.name = 'music_service' |
||||
|
s.version = '0.0.1' |
||||
|
s.summary = 'A new Flutter project.' |
||||
|
s.description = <<-DESC |
||||
|
A new Flutter project. |
||||
|
DESC |
||||
|
s.homepage = 'http://example.com' |
||||
|
s.license = { :file => '../LICENSE' } |
||||
|
s.author = { 'Your Company' => 'email@example.com' } |
||||
|
s.source = { :path => '.' } |
||||
|
s.source_files = 'Classes/**/*' |
||||
|
s.dependency 'Flutter' |
||||
|
s.platform = :ios, '12.0' |
||||
|
|
||||
|
# Flutter.framework does not contain a i386 slice. |
||||
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } |
||||
|
s.swift_version = '5.0' |
||||
|
|
||||
|
# If your plugin requires a privacy manifest, for example if it uses any |
||||
|
# required reason APIs, update the PrivacyInfo.xcprivacy file to describe your |
||||
|
# plugin's privacy impact, and then uncomment this line. For more information, |
||||
|
# see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files |
||||
|
# s.resource_bundles = {'music_service_privacy' => ['Resources/PrivacyInfo.xcprivacy']} |
||||
|
end |
||||
@ -0,0 +1,55 @@ |
|||||
|
import 'package:flutter/services.dart'; |
||||
|
|
||||
|
class MusicService { |
||||
|
static final MusicService _instance = MusicService._internal(); |
||||
|
|
||||
|
/// 获取单例 |
||||
|
factory MusicService() => _instance; |
||||
|
|
||||
|
// 方法通道 |
||||
|
final MethodChannel _methodChannel = const MethodChannel( |
||||
|
'com.yunqiinnovation.music_service', |
||||
|
); |
||||
|
|
||||
|
// 事件通道 - 简化为只使用数据和状态通道 |
||||
|
late final EventChannel _dataEventChannel; |
||||
|
late final EventChannel _statusEventChannel; |
||||
|
|
||||
|
// 事件流 |
||||
|
late final Stream<dynamic> _dataStream; |
||||
|
late final Stream<dynamic> _statusStream; |
||||
|
|
||||
|
// 获取事件流 |
||||
|
Stream<dynamic> get onDataReceived => _dataStream; |
||||
|
|
||||
|
// 状态流直接传递原始数据 |
||||
|
Stream<dynamic> get onStatusUpdated => _statusStream; |
||||
|
|
||||
|
// 内部构造函数 |
||||
|
MusicService._internal() { |
||||
|
_dataEventChannel = const EventChannel( |
||||
|
'com.yunqiinnovation.music_service/data_events', |
||||
|
); |
||||
|
_statusEventChannel = const EventChannel( |
||||
|
'com.yunqiinnovation.music_service/status_events', |
||||
|
); |
||||
|
|
||||
|
// 初始化事件流 |
||||
|
_dataStream = _dataEventChannel.receiveBroadcastStream(); |
||||
|
_statusStream = _statusEventChannel.receiveBroadcastStream(); |
||||
|
} |
||||
|
|
||||
|
Future<void> playMusic(String url) async { |
||||
|
await _methodChannel.invokeMethod('play', {'url': url}); |
||||
|
} |
||||
|
|
||||
|
Future<void> pauseMusic() async => _methodChannel.invokeMethod('pause'); |
||||
|
Future<void> resumeMusic() async => _methodChannel.invokeMethod('resume'); |
||||
|
Future<void> nextTrack() async => _methodChannel.invokeMethod('next'); |
||||
|
Future<void> previousTrack() async => _methodChannel.invokeMethod('previous'); |
||||
|
Future<void> stopMusic() async => _methodChannel.invokeMethod('stop'); |
||||
|
Future<void> setVolume(double volume) async => _methodChannel.invokeMethod( |
||||
|
'setVolume', |
||||
|
{'volume': volume}, |
||||
|
); // volume: 0.0~1.0 |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
name: music_service |
||||
|
description: "A new Flutter project." |
||||
|
version: 0.0.1 |
||||
|
homepage: |
||||
|
|
||||
|
environment: |
||||
|
sdk: ^3.7.2 |
||||
|
flutter: '>=3.3.0' |
||||
|
|
||||
|
dependencies: |
||||
|
flutter: |
||||
|
sdk: flutter |
||||
|
plugin_platform_interface: ^2.0.2 |
||||
|
|
||||
|
dev_dependencies: |
||||
|
flutter_test: |
||||
|
sdk: flutter |
||||
|
flutter_lints: ^5.0.0 |
||||
|
|
||||
|
# For information on the generic Dart part of this file, see the |
||||
|
# following page: https://dart.dev/tools/pub/pubspec |
||||
|
|
||||
|
# The following section is specific to Flutter packages. |
||||
|
flutter: |
||||
|
# This section identifies this Flutter project as a plugin project. |
||||
|
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) |
||||
|
# which should be registered in the plugin registry. This is required for |
||||
|
# using method channels. |
||||
|
# The Android 'package' specifies package in which the registered class is. |
||||
|
# This is required for using method channels on Android. |
||||
|
# The 'ffiPlugin' specifies that native code should be built and bundled. |
||||
|
# This is required for using `dart:ffi`. |
||||
|
# All these are used by the tooling to maintain consistency when |
||||
|
# adding or updating assets for this project. |
||||
|
plugin: |
||||
|
platforms: |
||||
|
android: |
||||
|
package: com.yunqiinnovation.music_service |
||||
|
pluginClass: MusicServicePlugin |
||||
|
ios: |
||||
|
pluginClass: MusicServicePlugin |
||||
|
|
||||
|
# To add assets to your plugin package, add an assets section, like this: |
||||
|
# assets: |
||||
|
# - images/a_dot_burr.jpeg |
||||
|
# - images/a_dot_ham.jpeg |
||||
|
# |
||||
|
# For details regarding assets in packages, see |
||||
|
# https://flutter.dev/to/asset-from-package |
||||
|
# |
||||
|
# An image asset can refer to one or more resolution-specific "variants", see |
||||
|
# https://flutter.dev/to/resolution-aware-images |
||||
|
|
||||
|
# To add custom fonts to your plugin package, add a fonts section here, |
||||
|
# in this "flutter" section. Each entry in this list should have a |
||||
|
# "family" key with the font family name, and a "fonts" key with a |
||||
|
# list giving the asset and other descriptors for the font. For |
||||
|
# example: |
||||
|
# fonts: |
||||
|
# - family: Schyler |
||||
|
# fonts: |
||||
|
# - asset: fonts/Schyler-Regular.ttf |
||||
|
# - asset: fonts/Schyler-Italic.ttf |
||||
|
# style: italic |
||||
|
# - family: Trajan Pro |
||||
|
# fonts: |
||||
|
# - asset: fonts/TrajanPro.ttf |
||||
|
# - asset: fonts/TrajanPro_Bold.ttf |
||||
|
# weight: 700 |
||||
|
# |
||||
|
# For details regarding fonts in packages, see |
||||
|
# https://flutter.dev/to/font-from-package |
||||
Loading…
Reference in new issue