mirror of
https://github.com/meditohq/medito-app.git
synced 2024-08-16 23:50:40 +03:00
Remove just_audio etc (#665)
* Play with exoplayer * Update Generate Riverpod Files.run.xml * Improve Android audio player * Update .gitignore * Update gradle and packages * Disable audio providers * Player listener * Fix player for android * Media notification * Fix merge * Fix endscreen * Fix player * Remove unused files * Update pubspec.yaml * Fix analysis issues * Fix pipeline
This commit is contained in:
parent
9efaaded97
commit
eebead2dbb
17
.github/workflows/flutter.yml
vendored
17
.github/workflows/flutter.yml
vendored
@ -65,13 +65,13 @@ jobs:
|
||||
flutter pub run dart_code_metrics:metrics check-unused-files --fatal-unused lib
|
||||
|
||||
# - name: Create or update PR comment
|
||||
# uses: peter-evans/create-or-update-comment@v1
|
||||
# with:
|
||||
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# repository: ${{ github.repository }}
|
||||
# issue-number: ${{ github.event.pull_request.number }}
|
||||
# body-file: metrics_report.txt
|
||||
# edit-mode: replace
|
||||
# uses: peter-evans/create-or-update-comment@v1
|
||||
# with:
|
||||
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# repository: ${{ github.repository }}
|
||||
# issue-number: ${{ github.event.pull_request.number }}
|
||||
# body-file: metrics_report.txt
|
||||
# edit-mode: replace
|
||||
|
||||
- name: Create a dummy .prod.env file
|
||||
run: echo -e "var BASE_URL = ''; \n var SENTRY_URL = ''; \nvar INIT_TOKEN = '';" > .prod.env
|
||||
@ -85,6 +85,9 @@ jobs:
|
||||
- name: Run Riverpod generator
|
||||
run: flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||
|
||||
- name: Run Pigeon generator
|
||||
run: flutter pub run pigeon --input pigeon_conf.dart
|
||||
|
||||
- name: Analyze
|
||||
run: flutter analyze --no-fatal-warnings --no-fatal-infos .
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -97,3 +97,4 @@ android/meditokey.jks
|
||||
ios/Runner.xcodeproj/project.pbxproj
|
||||
ios/Flutter/AppFrameworkInfo.plist
|
||||
android/app/src/google-services.json
|
||||
android/app/local.properties
|
||||
|
27
.metadata
27
.metadata
@ -4,8 +4,8 @@
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "5b12b7467fcbbdc7351d76690ce8a8693e804179"
|
||||
channel: "master"
|
||||
revision: "d211f42860350d914a5ad8102f9ec32764dc6d06"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
@ -13,11 +13,26 @@ project_type: app
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 5b12b7467fcbbdc7351d76690ce8a8693e804179
|
||||
base_revision: 5b12b7467fcbbdc7351d76690ce8a8693e804179
|
||||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
- platform: android
|
||||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
- platform: ios
|
||||
create_revision: 5b12b7467fcbbdc7351d76690ce8a8693e804179
|
||||
base_revision: 5b12b7467fcbbdc7351d76690ce8a8693e804179
|
||||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
- platform: linux
|
||||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
- platform: macos
|
||||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
- platform: web
|
||||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
- platform: windows
|
||||
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||
|
||||
# User provided section
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Generate Riverpod Files" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="dart run build_runner watch --delete-conflicting-outputs" />
|
||||
<option name="SCRIPT_TEXT" value="flutter pub run build_runner watch " />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
|
2
android/app/.gradle/config.properties
Normal file
2
android/app/.gradle/config.properties
Normal file
@ -0,0 +1,2 @@
|
||||
#Wed Jan 17 22:37:33 CET 2024
|
||||
java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home
|
@ -23,7 +23,7 @@ if (flutterVersionName == null) {
|
||||
|
||||
def appMinSdkVersion = localProperties.getProperty('flutter.minSdkVersion')
|
||||
if (appMinSdkVersion == null) {
|
||||
appMinSdkVersion = 21
|
||||
appMinSdkVersion = 26
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
@ -32,7 +32,7 @@ apply plugin: 'com.google.gms.google-services'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
compileSdkVersion 34
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
@ -110,6 +110,7 @@ android {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
namespace 'meditofoundation.medito'
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
eachDependency {
|
||||
@ -130,7 +131,12 @@ dependencies {
|
||||
implementation platform('com.google.firebase:firebase-bom:32.1.1')
|
||||
implementation 'com.google.firebase:firebase-analytics-ktx'
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
implementation 'androidx.media3:media3-session:1.2.0'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
implementation 'androidx.window:window:1.0.0'
|
||||
implementation 'androidx.window:window-java:1.0.0'
|
||||
|
||||
implementation "androidx.media3:media3-exoplayer:1.2.0"
|
||||
implementation "androidx.media3:media3-ui:1.2.0"
|
||||
implementation "androidx.media3:media3-common:1.2.0"
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="meditofoundation.medito">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
|
@ -1,6 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="meditofoundation.medito"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
@ -34,23 +32,13 @@
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="Medito"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<meta-data
|
||||
android:name="ic_audiofileplayer"
|
||||
android:value="drawable/logo" />
|
||||
|
||||
<activity
|
||||
android:name="com.ryanheise.audioservice.AudioServiceActivity"
|
||||
android:name=".MainActivity"
|
||||
android:allowBackup="false"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:exported="true"
|
||||
@ -60,89 +48,28 @@
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
|
||||
<!-- Deep linking -->
|
||||
<meta-data
|
||||
android:name="flutter_deeplinking_enabled"
|
||||
android:value="true" />
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="medito.app"
|
||||
android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
|
||||
<meta-data
|
||||
android:name="google_analytics_automatic_screen_reporting_enabled"
|
||||
android:value="false" />
|
||||
|
||||
<meta-data
|
||||
android:name="assets.audio.player.notification.icon"
|
||||
android:resource="@drawable/logo" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background" />
|
||||
|
||||
<!-- Theme to apply as soon as Flutter begins rendering frames -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/notification_icon_push" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_color"
|
||||
android:resource="@android:color/transparent" />
|
||||
|
||||
<service
|
||||
android:name="com.ryanheise.audioservice.AudioService"
|
||||
android:exported="true"
|
||||
tools:ignore="Instantiatable">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
|
||||
android:exported="true"
|
||||
tools:ignore="Instantiatable">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
android:value="high_importance_channel" />
|
||||
|
||||
<service
|
||||
android:name=".AudioPlayerService"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="mediaPlayback">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowser" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
</manifest>
|
@ -0,0 +1,516 @@
|
||||
// Autogenerated from Pigeon (v15.0.2), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
|
||||
|
||||
import android.util.Log
|
||||
import io.flutter.plugin.common.BasicMessageChannel
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.MessageCodec
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
private fun wrapResult(result: Any?): List<Any?> {
|
||||
return listOf(result)
|
||||
}
|
||||
|
||||
private fun wrapError(exception: Throwable): List<Any?> {
|
||||
if (exception is FlutterError) {
|
||||
return listOf(
|
||||
exception.code,
|
||||
exception.message,
|
||||
exception.details
|
||||
)
|
||||
} else {
|
||||
return listOf(
|
||||
exception.javaClass.simpleName,
|
||||
exception.toString(),
|
||||
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createConnectionError(channelName: String): FlutterError {
|
||||
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "")}
|
||||
|
||||
/**
|
||||
* Error class for passing custom error details to Flutter via a thrown PlatformException.
|
||||
* @property code The error code.
|
||||
* @property message The error message.
|
||||
* @property details The error details. Must be a datatype supported by the api codec.
|
||||
*/
|
||||
class FlutterError (
|
||||
val code: String,
|
||||
override val message: String? = null,
|
||||
val details: Any? = null
|
||||
) : Throwable()
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class AudioData (
|
||||
val url: String,
|
||||
val track: Track
|
||||
|
||||
) {
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun fromList(list: List<Any?>): AudioData {
|
||||
val url = list[0] as String
|
||||
val track = Track.fromList(list[1] as List<Any?>)
|
||||
return AudioData(url, track)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf<Any?>(
|
||||
url,
|
||||
track.toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class PlaybackState (
|
||||
val isPlaying: Boolean,
|
||||
val isBuffering: Boolean,
|
||||
val isSeeking: Boolean,
|
||||
val isCompleted: Boolean,
|
||||
val position: Long,
|
||||
val duration: Long,
|
||||
val speed: Speed,
|
||||
val volume: Long,
|
||||
val track: Track,
|
||||
val backgroundSound: BackgroundSound? = null
|
||||
|
||||
) {
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun fromList(list: List<Any?>): PlaybackState {
|
||||
val isPlaying = list[0] as Boolean
|
||||
val isBuffering = list[1] as Boolean
|
||||
val isSeeking = list[2] as Boolean
|
||||
val isCompleted = list[3] as Boolean
|
||||
val position = list[4].let { if (it is Int) it.toLong() else it as Long }
|
||||
val duration = list[5].let { if (it is Int) it.toLong() else it as Long }
|
||||
val speed = Speed.fromList(list[6] as List<Any?>)
|
||||
val volume = list[7].let { if (it is Int) it.toLong() else it as Long }
|
||||
val track = Track.fromList(list[8] as List<Any?>)
|
||||
val backgroundSound: BackgroundSound? = (list[9] as List<Any?>?)?.let {
|
||||
BackgroundSound.fromList(it)
|
||||
}
|
||||
return PlaybackState(isPlaying, isBuffering, isSeeking, isCompleted, position, duration, speed, volume, track, backgroundSound)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf<Any?>(
|
||||
isPlaying,
|
||||
isBuffering,
|
||||
isSeeking,
|
||||
isCompleted,
|
||||
position,
|
||||
duration,
|
||||
speed.toList(),
|
||||
volume,
|
||||
track.toList(),
|
||||
backgroundSound?.toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class BackgroundSound (
|
||||
val uri: String? = null,
|
||||
val title: String
|
||||
|
||||
) {
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun fromList(list: List<Any?>): BackgroundSound {
|
||||
val uri = list[0] as String?
|
||||
val title = list[1] as String
|
||||
return BackgroundSound(uri, title)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf<Any?>(
|
||||
uri,
|
||||
title,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class Speed (
|
||||
val speed: Double
|
||||
|
||||
) {
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun fromList(list: List<Any?>): Speed {
|
||||
val speed = list[0] as Double
|
||||
return Speed(speed)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf<Any?>(
|
||||
speed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class Track (
|
||||
val title: String,
|
||||
val description: String,
|
||||
val imageUrl: String,
|
||||
val artist: String? = null,
|
||||
val artistUrl: String? = null
|
||||
|
||||
) {
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun fromList(list: List<Any?>): Track {
|
||||
val title = list[0] as String
|
||||
val description = list[1] as String
|
||||
val imageUrl = list[2] as String
|
||||
val artist = list[3] as String?
|
||||
val artistUrl = list[4] as String?
|
||||
return Track(title, description, imageUrl, artist, artistUrl)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf<Any?>(
|
||||
title,
|
||||
description,
|
||||
imageUrl,
|
||||
artist,
|
||||
artistUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private object MeditoAudioServiceApiCodec : StandardMessageCodec() {
|
||||
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||
return when (type) {
|
||||
128.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
AudioData.fromList(it)
|
||||
}
|
||||
}
|
||||
129.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
Track.fromList(it)
|
||||
}
|
||||
}
|
||||
else -> super.readValueOfType(type, buffer)
|
||||
}
|
||||
}
|
||||
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
|
||||
when (value) {
|
||||
is AudioData -> {
|
||||
stream.write(128)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is Track -> {
|
||||
stream.write(129)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
else -> super.writeValue(stream, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface MeditoAudioServiceApi {
|
||||
fun playAudio(audioData: AudioData): Boolean
|
||||
fun playPauseAudio()
|
||||
fun stopAudio()
|
||||
fun setSpeed(speed: Double)
|
||||
fun seekToPosition(position: Long)
|
||||
fun skip10SecondsForward()
|
||||
fun skip10SecondsBackward()
|
||||
fun setBackgroundSound(uri: String?)
|
||||
fun setBackgroundSoundVolume(volume: Double)
|
||||
fun stopBackgroundSound()
|
||||
fun playBackgroundSound()
|
||||
|
||||
companion object {
|
||||
/** The codec used by MeditoAudioServiceApi. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
MeditoAudioServiceApiCodec
|
||||
}
|
||||
/** Sets up an instance of `MeditoAudioServiceApi` to handle messages through the `binaryMessenger`. */
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun setUp(binaryMessenger: BinaryMessenger, api: MeditoAudioServiceApi?) {
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.playAudio", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val audioDataArg = args[0] as AudioData
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
wrapped = listOf<Any?>(api.playAudio(audioDataArg))
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.playPauseAudio", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.playPauseAudio()
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.stopAudio", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.stopAudio()
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.setSpeed", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val speedArg = args[0] as Double
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.setSpeed(speedArg)
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.seekToPosition", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val positionArg = args[0].let { if (it is Int) it.toLong() else it as Long }
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.seekToPosition(positionArg)
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.skip10SecondsForward", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.skip10SecondsForward()
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.skip10SecondsBackward", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.skip10SecondsBackward()
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.setBackgroundSound", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val uriArg = args[0] as String?
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.setBackgroundSound(uriArg)
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.setBackgroundSoundVolume", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val volumeArg = args[0] as Double
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.setBackgroundSoundVolume(volumeArg)
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.stopBackgroundSound", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.stopBackgroundSound()
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.Medito.MeditoAudioServiceApi.playBackgroundSound", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
var wrapped: List<Any?>
|
||||
try {
|
||||
api.playBackgroundSound()
|
||||
wrapped = listOf<Any?>(null)
|
||||
} catch (exception: Throwable) {
|
||||
wrapped = wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private object MeditoAudioServiceCallbackApiCodec : StandardMessageCodec() {
|
||||
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||
return when (type) {
|
||||
128.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
BackgroundSound.fromList(it)
|
||||
}
|
||||
}
|
||||
129.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
PlaybackState.fromList(it)
|
||||
}
|
||||
}
|
||||
130.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
Speed.fromList(it)
|
||||
}
|
||||
}
|
||||
131.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
Track.fromList(it)
|
||||
}
|
||||
}
|
||||
else -> super.readValueOfType(type, buffer)
|
||||
}
|
||||
}
|
||||
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
|
||||
when (value) {
|
||||
is BackgroundSound -> {
|
||||
stream.write(128)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is PlaybackState -> {
|
||||
stream.write(129)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is Speed -> {
|
||||
stream.write(130)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is Track -> {
|
||||
stream.write(131)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
else -> super.writeValue(stream, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class MeditoAudioServiceCallbackApi(private val binaryMessenger: BinaryMessenger) {
|
||||
companion object {
|
||||
/** The codec used by MeditoAudioServiceCallbackApi. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
MeditoAudioServiceCallbackApiCodec
|
||||
}
|
||||
}
|
||||
fun updatePlaybackState(stateArg: PlaybackState, callback: (Result<Unit>) -> Unit) {
|
||||
val channelName = "dev.flutter.pigeon.Medito.MeditoAudioServiceCallbackApi.updatePlaybackState"
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
|
||||
channel.send(listOf(stateArg)) {
|
||||
if (it is List<*>) {
|
||||
if (it.size > 1) {
|
||||
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
|
||||
} else {
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
} else {
|
||||
callback(Result.failure(createConnectionError(channelName)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,308 @@
|
||||
package meditofoundation.medito
|
||||
|
||||
import AudioData
|
||||
import MeditoAudioServiceApi
|
||||
import MeditoAudioServiceCallbackApi
|
||||
import PlaybackState
|
||||
import Speed
|
||||
import Track
|
||||
import android.app.Notification
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.NotificationUtil
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.session.MediaSession
|
||||
import androidx.media3.session.MediaSessionService
|
||||
import androidx.media3.session.MediaStyleNotificationHelper
|
||||
import io.flutter.embedding.engine.FlutterEngineCache
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class AudioPlayerService : MediaSessionService(), Player.Listener, MeditoAudioServiceApi,
|
||||
MediaSession.Callback {
|
||||
|
||||
private var backgroundMusicVolume: Float = 0.0F
|
||||
private var backgroundSoundUri: String? = null
|
||||
private lateinit var primaryPlayer: ExoPlayer
|
||||
private lateinit var backgroundMusicPlayer: ExoPlayer
|
||||
private var primaryMediaSession: MediaSession? = null
|
||||
private var meditoAudioApi: MeditoAudioServiceCallbackApi? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
primaryPlayer = ExoPlayer.Builder(this)
|
||||
.setAudioAttributes(AudioAttributes.DEFAULT, false)
|
||||
.setHandleAudioBecomingNoisy(true)
|
||||
.setWakeMode(C.WAKE_MODE_NETWORK)
|
||||
.build()
|
||||
|
||||
backgroundMusicPlayer = ExoPlayer.Builder(this)
|
||||
.setAudioAttributes(AudioAttributes.DEFAULT, false)
|
||||
.setHandleAudioBecomingNoisy(true)
|
||||
.setWakeMode(C.WAKE_MODE_NETWORK)
|
||||
.build()
|
||||
|
||||
primaryPlayer.addListener(this)
|
||||
|
||||
primaryMediaSession = MediaSession.Builder(this, primaryPlayer)
|
||||
.setCallback(this)
|
||||
.build()
|
||||
|
||||
FlutterEngineCache.getInstance().get(MainActivity.ENGINE_ID)?.let { engine ->
|
||||
MeditoAudioServiceApi.setUp(engine.dartExecutor.binaryMessenger, this)
|
||||
meditoAudioApi = MeditoAudioServiceCallbackApi(engine.dartExecutor.binaryMessenger)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
handler.removeCallbacks(positionUpdateRunnable)
|
||||
|
||||
primaryPlayer.removeListener(this)
|
||||
backgroundMusicPlayer.removeListener(this)
|
||||
|
||||
primaryMediaSession?.run {
|
||||
player.release()
|
||||
release()
|
||||
primaryMediaSession = null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
handler.removeCallbacks(positionUpdateRunnable)
|
||||
|
||||
val player = primaryMediaSession?.player!!
|
||||
if (!player.playWhenReady || player.mediaItemCount == 0) {
|
||||
this.primaryPlayer.clearMediaItems()
|
||||
this.backgroundMusicPlayer.clearMediaItems()
|
||||
this.primaryPlayer.stop()
|
||||
this.backgroundMusicPlayer.stop()
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
|
||||
return primaryMediaSession
|
||||
}
|
||||
|
||||
override fun playAudio(audioData: AudioData): Boolean {
|
||||
|
||||
val primaryMediaItem = MediaItem.Builder()
|
||||
.setUri(audioData.url)
|
||||
.setMediaId(audioData.url)
|
||||
.setMediaMetadata(
|
||||
MediaMetadata.Builder()
|
||||
.setTitle(audioData.track.title)
|
||||
.setArtist(audioData.track.artist)
|
||||
.setDescription(audioData.track.description)
|
||||
.setArtworkUri(Uri.parse(audioData.track.imageUrl))
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
|
||||
primaryPlayer.setMediaItem(primaryMediaItem)
|
||||
primaryPlayer.prepare()
|
||||
primaryPlayer.play()
|
||||
|
||||
handler.postDelayed(positionUpdateRunnable, 500)
|
||||
|
||||
playBackgroundSound()
|
||||
showNotification()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
private fun createMediaNotification(
|
||||
session: MediaSession?,
|
||||
artworkBitmap: Bitmap?
|
||||
): Notification {
|
||||
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle(primaryPlayer.currentMediaItem?.mediaMetadata?.title ?: "Medito")
|
||||
.setContentText(primaryPlayer.currentMediaItem?.mediaMetadata?.artist ?: "Medito")
|
||||
.setSmallIcon(R.drawable.notification_icon_push)
|
||||
.setLargeIcon(artworkBitmap)
|
||||
.setStyle(session?.let { MediaStyleNotificationHelper.MediaStyle(it) })
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
private fun showNotification() {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
// Move player access to the main thread
|
||||
val artworkUri = primaryPlayer.currentMediaItem?.mediaMetadata?.artworkUri
|
||||
val artworkBitmap = withContext(Dispatchers.IO) {
|
||||
// Download the bitmap in the background thread
|
||||
artworkUri?.let { downloadBitmap(it) }
|
||||
}
|
||||
val notification = createMediaNotification(primaryMediaSession, artworkBitmap)
|
||||
try {
|
||||
NotificationUtil.setNotification(
|
||||
this@AudioPlayerService,
|
||||
NOTIFICATION_ID,
|
||||
notification
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun downloadBitmap(uri: Uri): Bitmap? = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val inputStream = java.net.URL(uri.toString()).openStream()
|
||||
BitmapFactory.decodeStream(inputStream)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun playBackgroundSound() {
|
||||
if (backgroundSoundUri == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val backgroundMediaItem = MediaItem.Builder()
|
||||
.setUri(backgroundSoundUri)
|
||||
.build()
|
||||
|
||||
backgroundMusicPlayer.repeatMode = Player.REPEAT_MODE_ONE
|
||||
backgroundMusicPlayer.setMediaItem(backgroundMediaItem)
|
||||
backgroundMusicPlayer.prepare()
|
||||
backgroundMusicPlayer.play()
|
||||
}
|
||||
|
||||
override fun setBackgroundSound(uri: String?) {
|
||||
this.backgroundSoundUri = uri
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
override fun stopBackgroundSound() {
|
||||
backgroundMusicPlayer.stop()
|
||||
NotificationUtil.setNotification(
|
||||
this,
|
||||
NOTIFICATION_ID,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
override fun setBackgroundSoundVolume(volume: Double) {
|
||||
this.backgroundMusicVolume = volume.toFloat() / 100
|
||||
backgroundMusicPlayer.volume = this.backgroundMusicVolume
|
||||
}
|
||||
|
||||
override fun seekToPosition(position: Long) {
|
||||
primaryPlayer.seekTo(position)
|
||||
}
|
||||
|
||||
override fun setSpeed(speed: Double) {
|
||||
primaryPlayer.setPlaybackSpeed(speed.toFloat())
|
||||
}
|
||||
|
||||
override fun skip10SecondsForward() {
|
||||
if (primaryPlayer.currentPosition + 10000 > primaryPlayer.duration) {
|
||||
primaryPlayer.seekTo(primaryPlayer.duration)
|
||||
return
|
||||
}
|
||||
primaryPlayer.seekTo(primaryPlayer.currentPosition + 10000)
|
||||
}
|
||||
|
||||
override fun skip10SecondsBackward() {
|
||||
if (primaryPlayer.duration - primaryPlayer.currentPosition < 10000) {
|
||||
primaryPlayer.seekTo(0)
|
||||
return
|
||||
}
|
||||
primaryPlayer.seekTo(primaryPlayer.currentPosition - 10000)
|
||||
}
|
||||
|
||||
override fun stopAudio() {
|
||||
primaryPlayer.stop()
|
||||
backgroundMusicPlayer.stop()
|
||||
handler.removeCallbacks(positionUpdateRunnable)
|
||||
|
||||
}
|
||||
|
||||
override fun playPauseAudio() {
|
||||
if (primaryPlayer.isPlaying) {
|
||||
primaryPlayer.pause()
|
||||
backgroundMusicPlayer.pause()
|
||||
} else {
|
||||
primaryPlayer.play()
|
||||
backgroundMusicPlayer.play()
|
||||
}
|
||||
}
|
||||
|
||||
private val fadeOutDurationMillis = 10000
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val positionUpdateRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
|
||||
val currentPosition = primaryPlayer.currentPosition
|
||||
val trackDuration = primaryPlayer.duration
|
||||
|
||||
applyBackgroundSoundVolume(trackDuration, currentPosition)
|
||||
|
||||
val state = PlaybackState(
|
||||
isPlaying = primaryPlayer.isPlaying,
|
||||
position = primaryPlayer.currentPosition,
|
||||
volume = (primaryPlayer.volume * 100).toLong(),
|
||||
speed = Speed(primaryPlayer.playbackParameters.speed.toDouble()),
|
||||
isBuffering = primaryPlayer.playbackState == Player.STATE_BUFFERING,
|
||||
duration = primaryPlayer.duration,
|
||||
isSeeking = primaryPlayer.playbackState == Player.STATE_BUFFERING,
|
||||
isCompleted = primaryPlayer.playbackState == Player.STATE_ENDED,
|
||||
track = Track(
|
||||
title = primaryPlayer.currentMediaItem?.mediaMetadata?.title.toString(),
|
||||
description = primaryPlayer.currentMediaItem?.mediaMetadata?.description.toString(),
|
||||
imageUrl = primaryPlayer.currentMediaItem?.mediaMetadata?.artworkUri.toString(),
|
||||
artist = primaryPlayer.currentMediaItem?.mediaMetadata?.artist.toString(),
|
||||
),
|
||||
)
|
||||
|
||||
meditoAudioApi?.updatePlaybackState(state) {
|
||||
if (primaryPlayer.playbackState != Player.STATE_ENDED) {
|
||||
handler.postDelayed(this, 250)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun applyBackgroundSoundVolume(trackDuration: Long, currentPosition: Long) {
|
||||
if (trackDuration - currentPosition <= fadeOutDurationMillis && trackDuration > fadeOutDurationMillis) {
|
||||
val volumeFraction =
|
||||
(trackDuration - currentPosition).toFloat() / fadeOutDurationMillis
|
||||
backgroundMusicPlayer.volume =
|
||||
backgroundMusicVolume * volumeFraction
|
||||
} else {
|
||||
backgroundMusicPlayer.volume = backgroundMusicVolume
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "medito_media_channel"
|
||||
const val NOTIFICATION_ID = 101011
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,47 @@
|
||||
package meditofoundation.medito
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.embedding.engine.FlutterEngineCache
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine)
|
||||
FlutterEngineCache
|
||||
.getInstance()
|
||||
.put(ENGINE_ID, flutterEngine);
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
createNotificationChannel()
|
||||
|
||||
val intent = Intent(this, AudioPlayerService::class.java)
|
||||
startService(intent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ENGINE_ID = "medito_flutter_engine"
|
||||
}
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
val channelName = "Meditation audio"
|
||||
val importance = NotificationManager.IMPORTANCE_DEFAULT
|
||||
val channel = NotificationChannel(AudioPlayerService.CHANNEL_ID, channelName, importance).apply {
|
||||
description = "Notification for media control of meditation audio"
|
||||
}
|
||||
|
||||
val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="meditofoundation.medito">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
@ -6,8 +6,8 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||
classpath 'com.google.gms:google-services:4.3.15'
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath 'com.google.gms:google-services:4.4.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
4
lib/constants/environments/environment_constants.dart
Normal file
4
lib/constants/environments/environment_constants.dart
Normal file
@ -0,0 +1,4 @@
|
||||
class EnvironmentConstants {
|
||||
static const String stagingEnv = '.staging.env';
|
||||
static const String prodEnv = '.prod.env';
|
||||
}
|
@ -10,8 +10,6 @@ class StringConstants {
|
||||
static const String begin = 'Begin';
|
||||
static const String none = 'None';
|
||||
static const String removed = 'Removed';
|
||||
static const String stagingEnv = '.staging.env';
|
||||
static const String prodEnv = '.prod.env';
|
||||
static const String supportEmail = 'hello@meditofoundation.org';
|
||||
static const String pickNarratorAndDuration = 'Pick a narrator & duration';
|
||||
static const String pickDuration = 'Pick a duration';
|
||||
@ -48,8 +46,6 @@ class StringConstants {
|
||||
static const String x08 = 'X0.8';
|
||||
static const String x09 = 'X0.9';
|
||||
static const String x1 = 'X1';
|
||||
static const String x125 = 'X1.25';
|
||||
static const String x15 = 'X1.5';
|
||||
|
||||
//Join
|
||||
static const String joinTheMeditoFamily = 'Join the Medito Family';
|
||||
|
@ -12,20 +12,20 @@ const fontSize16 = 16.0;
|
||||
const fontSize14 = 14.0;
|
||||
const smallScreenWidth = 480.0;
|
||||
const miniPlayerHeight = 56.0;
|
||||
const height32 = SizedBox(height: 32);
|
||||
const height24 = SizedBox(height: 24);
|
||||
const width2 = SizedBox(width: 2);
|
||||
const width4 = SizedBox(width: 4);
|
||||
const width8 = SizedBox(width: 8);
|
||||
const width12 = SizedBox(width: 12);
|
||||
const width16 = SizedBox(width: 16);
|
||||
const height4 = SizedBox(height: 4);
|
||||
const height5 = SizedBox(height: 5);
|
||||
const height6 = SizedBox(height: 6);
|
||||
const height8 = SizedBox(height: 8);
|
||||
const height12 = SizedBox(height: 12);
|
||||
const height16 = SizedBox(height: 16);
|
||||
const height20 = SizedBox(height: 20);
|
||||
const height12 = SizedBox(height: 12);
|
||||
const width16 = SizedBox(width: 16);
|
||||
const width12 = SizedBox(width: 12);
|
||||
const height8 = SizedBox(height: 8);
|
||||
const height6 = SizedBox(height: 6);
|
||||
const height5 = SizedBox(height: 5);
|
||||
const height4 = SizedBox(height: 4);
|
||||
const width8 = SizedBox(width: 8);
|
||||
const width4 = SizedBox(width: 4);
|
||||
const width2 = SizedBox(width: 2);
|
||||
const height24 = SizedBox(height: 24);
|
||||
const height32 = SizedBox(height: 32);
|
||||
const bottomSheetBoxDecoration = BoxDecoration(
|
||||
color: ColorConstants.onyx,
|
||||
borderRadius: BorderRadius.only(
|
||||
|
@ -18,10 +18,9 @@ import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/constants/theme/app_theme.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/routes/routes.dart';
|
||||
import 'package:Medito/src/audio_pigeon.g.dart';
|
||||
import 'package:Medito/utils/stats_utils.dart';
|
||||
import 'package:Medito/utils/utils.dart';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:audio_session/audio_session.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -31,13 +30,17 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_web_plugins/url_strategy.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import 'constants/environments/environment_constants.dart';
|
||||
import 'services/notifications/notifications_service.dart';
|
||||
|
||||
late AudioPlayerNotifier audioHandler;
|
||||
var audioStateNotifier = AudioStateNotifier();
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await dotenv.load(fileName: StringConstants.stagingEnv);
|
||||
|
||||
await dotenv.load(fileName: EnvironmentConstants.stagingEnv);
|
||||
|
||||
MeditoAudioServiceCallbackApi.setup(AudioStateProvider(audioStateNotifier));
|
||||
|
||||
var sharedPreferences = await initializeSharedPreferences();
|
||||
var isPlayServices = await checkGooglePlayServices();
|
||||
@ -56,8 +59,6 @@ Future<void> main() async {
|
||||
},
|
||||
);
|
||||
|
||||
audioHandler = await initAudioService();
|
||||
|
||||
usePathUrlStrategy();
|
||||
|
||||
runApp(
|
||||
@ -70,36 +71,6 @@ Future<void> main() async {
|
||||
);
|
||||
}
|
||||
|
||||
Future<AudioPlayerNotifier> initAudioService() async {
|
||||
final session = await AudioSession.instance;
|
||||
await session.configure(const AudioSessionConfiguration(
|
||||
avAudioSessionCategory: AVAudioSessionCategory.playback,
|
||||
avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.duckOthers,
|
||||
avAudioSessionMode: AVAudioSessionMode.defaultMode,
|
||||
avAudioSessionRouteSharingPolicy:
|
||||
AVAudioSessionRouteSharingPolicy.defaultPolicy,
|
||||
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
|
||||
androidAudioAttributes: AndroidAudioAttributes(
|
||||
contentType: AndroidAudioContentType.music,
|
||||
flags: AndroidAudioFlags.none,
|
||||
usage: AndroidAudioUsage.media,
|
||||
),
|
||||
androidAudioFocusGainType: AndroidAudioFocusGainType.gainTransientMayDuck,
|
||||
androidWillPauseWhenDucked: true,
|
||||
));
|
||||
|
||||
return await AudioService.init(
|
||||
builder: () => AudioPlayerNotifier(),
|
||||
config: AudioServiceConfig(
|
||||
androidNotificationChannelId: 'com.medito.app.channel.audio',
|
||||
androidNotificationChannelName: 'Medito Meditation',
|
||||
androidNotificationOngoing: true,
|
||||
androidStopForegroundOnPause: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// This Widget is the main application widget.
|
||||
// ignore: prefer-match-file-name
|
||||
class ParentWidget extends ConsumerStatefulWidget {
|
||||
static const String _title = 'Medito';
|
||||
@ -116,12 +87,6 @@ class _ParentWidgetState extends ConsumerState<ParentWidget>
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
unawaited(updateStatsFromBg(ref));
|
||||
} else if (state == AppLifecycleState.detached) {
|
||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||
audioProvider.stop();
|
||||
audioProvider.trackAudioPlayer.dispose();
|
||||
audioProvider.backgroundSoundAudioPlayer.dispose();
|
||||
audioProvider.dispose();
|
||||
}
|
||||
currentState = state;
|
||||
}
|
||||
@ -129,10 +94,6 @@ class _ParentWidgetState extends ConsumerState<ParentWidget>
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||
audioProvider.trackAudioPlayer.dispose();
|
||||
audioProvider.backgroundSoundAudioPlayer.dispose();
|
||||
audioProvider.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -155,15 +116,9 @@ class _ParentWidgetState extends ConsumerState<ParentWidget>
|
||||
);
|
||||
onMessageAppOpened(context, ref);
|
||||
initializeNotification(context, ref);
|
||||
initializeAudioPlayer();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
void initializeAudioPlayer() {
|
||||
var audioPlayerProvider = ref.read(audioPlayerNotifierProvider);
|
||||
audioPlayerProvider.initAudioHandler();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final goRouter = ref.watch(goRouterProvider);
|
||||
|
@ -16,20 +16,20 @@ export 'events/pack_viewed/pack_viewed_model.dart';
|
||||
export 'events/save_fcm_token/save_fcm_token_model.dart';
|
||||
export 'events/track_viewed/track_viewed_model.dart';
|
||||
export 'events/transfer_stats/transfer_stats_model.dart';
|
||||
export 'explore/explore_model.dart';
|
||||
export 'home/announcement/announcement_model.dart';
|
||||
export 'home/chips/home_chips_items_model.dart';
|
||||
export 'home/editorial/editorial_model.dart';
|
||||
export 'home/header/home_header_model.dart';
|
||||
export 'home/home_model.dart';
|
||||
export 'home/menu/home_menu_model.dart';
|
||||
export 'home/quote/quote_model.dart';
|
||||
export 'home/editorial/editorial_model.dart';
|
||||
export 'home/rows/home_rows_model.dart';
|
||||
export 'home/shortcuts/shortcuts_model.dart';
|
||||
export 'join/join_route_params_model.dart';
|
||||
export 'me/me_model.dart';
|
||||
export 'notification/notification_payload_model.dart';
|
||||
export 'pack/pack_model.dart';
|
||||
export 'explore/explore_model.dart';
|
||||
export 'stats/all_stats/all_stats_model.dart';
|
||||
export 'stats/stats_model.dart';
|
||||
export 'stats/tiles/tiles_model.dart';
|
||||
|
@ -10,7 +10,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'background_sounds_provider.g.dart';
|
||||
import '../../src/audio_pigeon.g.dart';
|
||||
|
||||
part 'background_sounds_notifier.g.dart';
|
||||
|
||||
final _api = MeditoAudioServiceApi();
|
||||
|
||||
@riverpod
|
||||
Future<List<BackgroundSoundsModel>> backgroundSounds(BackgroundSoundsRef ref) {
|
||||
@ -43,7 +47,7 @@ final backgroundSoundsNotifierProvider =
|
||||
//ignore:prefer-match-file-name
|
||||
class BackgroundSoundsNotifier extends ChangeNotifier {
|
||||
final Ref ref;
|
||||
double volume = 0;
|
||||
double volume = 50;
|
||||
BackgroundSoundsModel? selectedBgSound;
|
||||
|
||||
BackgroundSoundsNotifier(this.ref);
|
||||
@ -51,18 +55,21 @@ class BackgroundSoundsNotifier extends ChangeNotifier {
|
||||
void handleOnChangeVolume(double vol) {
|
||||
volume = vol;
|
||||
ref.read(backgroundSoundsRepositoryProvider).handleOnChangeVolume(vol);
|
||||
_api.setBackgroundSoundVolume(volume);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void handleOnChangeSound(BackgroundSoundsModel? sound) {
|
||||
selectedBgSound = sound;
|
||||
var bgSoundRepoProvider = ref.read(backgroundSoundsRepositoryProvider);
|
||||
|
||||
if (sound != null) {
|
||||
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
||||
bgSoundRepoProvider.handleOnChangeSound(sound);
|
||||
updateItemsInSavedBgSoundList(sound);
|
||||
_updateItemsInSavedBgSoundList(sound);
|
||||
|
||||
if (sound.title != StringConstants.none) {
|
||||
var name = '${sound.title}.mp3';
|
||||
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
||||
downloadAudio.getDownloadedFile(name).then((value) {
|
||||
if (value == null) {
|
||||
downloadAudio.downloadFile(
|
||||
@ -70,15 +77,25 @@ class BackgroundSoundsNotifier extends ChangeNotifier {
|
||||
name: name,
|
||||
);
|
||||
}
|
||||
_api.setBackgroundSound(value);
|
||||
_api.playBackgroundSound();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
bgSoundRepoProvider.removeSelectedBgSound();
|
||||
stopBackgroundSound();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void updateItemsInSavedBgSoundList(BackgroundSoundsModel sound) {
|
||||
void stopBackgroundSound() {
|
||||
var bgSoundRepoProvider = ref.read(backgroundSoundsRepositoryProvider);
|
||||
bgSoundRepoProvider.removeSelectedBgSound();
|
||||
_api.setBackgroundSound(null);
|
||||
_api.stopBackgroundSound();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _updateItemsInSavedBgSoundList(BackgroundSoundsModel sound) {
|
||||
final provider = ref.read(backgroundSoundsRepositoryProvider);
|
||||
provider.updateItemsInSavedBgSoundList(sound);
|
||||
}
|
||||
@ -94,12 +111,9 @@ class BackgroundSoundsNotifier extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void getVolumeFromPref() {
|
||||
var value = ref.read(backgroundSoundsRepositoryProvider).getBgSoundVolume();
|
||||
if (value != null) {
|
||||
volume = value;
|
||||
notifyListeners();
|
||||
} else {
|
||||
handleOnChangeVolume(50.0);
|
||||
}
|
||||
var value =
|
||||
ref.read(backgroundSoundsRepositoryProvider).getBgSoundVolume() ?? 50.0;
|
||||
handleOnChangeVolume(value);
|
||||
volume = value;
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/repositories/repositories.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import '../me/me_provider.dart';
|
||||
|
||||
part 'device_and_app_info_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
|
@ -1,23 +0,0 @@
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final audioPlayPauseStateProvider = StateProvider<PLAY_PAUSE_AUDIO>(
|
||||
(ref) {
|
||||
return PLAY_PAUSE_AUDIO.PAUSE;
|
||||
},
|
||||
);
|
||||
|
||||
final audioPlayPauseProvider = Provider<void>(
|
||||
(ref) {
|
||||
final audioPlayer = ref.watch(audioPlayerNotifierProvider);
|
||||
final currentState = ref.watch(audioPlayPauseStateProvider);
|
||||
if (currentState == PLAY_PAUSE_AUDIO.PLAY) {
|
||||
audioPlayer.play();
|
||||
} else {
|
||||
audioPlayer.pause();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
//ignore: prefer-match-file-name
|
||||
enum PLAY_PAUSE_AUDIO { PLAY, PAUSE }
|
@ -1,251 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:Medito/main.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
final audioPlayerNotifierProvider =
|
||||
ChangeNotifierProvider<AudioPlayerNotifier>((ref) {
|
||||
return audioHandler;
|
||||
});
|
||||
|
||||
//ignore:prefer-match-file-name
|
||||
class AudioPlayerNotifier extends BaseAudioHandler
|
||||
with QueueHandler, SeekHandler, ChangeNotifier {
|
||||
var backgroundSoundAudioPlayer = AudioPlayer();
|
||||
final trackAudioPlayer = AudioPlayer();
|
||||
|
||||
TrackFilesModel? currentlyPlayingTrack;
|
||||
final hasBgSoundKey = 'hasBgSound';
|
||||
final fadeDurationInSeconds = 5;
|
||||
var bgVolume;
|
||||
|
||||
@override
|
||||
Future<void> pause() async {
|
||||
try {
|
||||
pauseBackgroundSound();
|
||||
unawaited(trackAudioPlayer.pause());
|
||||
} catch (err) {
|
||||
unawaited(Sentry.captureException(
|
||||
err,
|
||||
stackTrace: err,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> play() async {
|
||||
try {
|
||||
unawaited(trackAudioPlayer.play());
|
||||
var hasBgSound = mediaItemHasBGSound();
|
||||
if (hasBgSound) {
|
||||
playBackgroundSound();
|
||||
} else {
|
||||
pauseBackgroundSound();
|
||||
}
|
||||
} catch (err) {
|
||||
unawaited(Sentry.captureException(
|
||||
err,
|
||||
stackTrace: err,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() async {
|
||||
unawaited(trackAudioPlayer.stop());
|
||||
if (mediaItemHasBGSound()) {
|
||||
stopBackgroundSound();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> seek(Duration position) async {
|
||||
seekValueFromSlider(position.inMilliseconds);
|
||||
}
|
||||
|
||||
void initAudioHandler() {
|
||||
trackAudioPlayer.playbackEventStream
|
||||
.map(_transformEvent)
|
||||
.pipe(playbackState);
|
||||
}
|
||||
|
||||
void setBackgroundAudio(BackgroundSoundsModel sound) {
|
||||
getApplicationDocumentsDirectory().then((file) async {
|
||||
var savePath = file.path + '/${sound.title}.mp3';
|
||||
var filePath = File(savePath);
|
||||
if (await filePath.exists()) {
|
||||
unawaited(backgroundSoundAudioPlayer.setFilePath(filePath.path));
|
||||
} else {
|
||||
unawaited(
|
||||
backgroundSoundAudioPlayer.setAudioSource(
|
||||
AudioSource.uri(
|
||||
Uri.parse(sound.path),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setTrackAudio(
|
||||
TrackModel trackModel,
|
||||
TrackFilesModel file, {
|
||||
String? filePath,
|
||||
}) {
|
||||
try {
|
||||
if (filePath != null) {
|
||||
unawaited(trackAudioPlayer.setFilePath(filePath));
|
||||
setMediaItem(trackModel, file, filePath: filePath);
|
||||
} else {
|
||||
setMediaItem(trackModel, file);
|
||||
trackAudioPlayer.setUrl(file.path);
|
||||
}
|
||||
} catch (e) {
|
||||
unawaited(Sentry.captureException(
|
||||
e,
|
||||
stackTrace: e,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void playBackgroundSound() {
|
||||
backgroundSoundAudioPlayer.play();
|
||||
backgroundSoundAudioPlayer.setLoopMode(LoopMode.all);
|
||||
}
|
||||
|
||||
void pauseBackgroundSound() {
|
||||
backgroundSoundAudioPlayer.pause();
|
||||
}
|
||||
|
||||
void stopBackgroundSound() {
|
||||
backgroundSoundAudioPlayer.stop();
|
||||
}
|
||||
|
||||
void setTrackAudioSpeed(double speed) {
|
||||
trackAudioPlayer.setSpeed(speed);
|
||||
}
|
||||
|
||||
void seekValueFromSlider(int duration) {
|
||||
try {
|
||||
trackAudioPlayer.seek(Duration(milliseconds: duration));
|
||||
} catch (err) {
|
||||
Sentry.captureException(
|
||||
err,
|
||||
stackTrace: err,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void skipForward10Secs() async {
|
||||
var seekDuration = trackAudioPlayer.position.inMilliseconds +
|
||||
Duration(seconds: 10).inMilliseconds;
|
||||
await trackAudioPlayer.seek(Duration(milliseconds: seekDuration));
|
||||
}
|
||||
|
||||
void skipBackward10Secs() async {
|
||||
var seekDuration = max(
|
||||
0,
|
||||
trackAudioPlayer.position.inMilliseconds -
|
||||
Duration(seconds: 10).inMilliseconds,
|
||||
);
|
||||
await trackAudioPlayer.seek(Duration(milliseconds: seekDuration));
|
||||
}
|
||||
|
||||
void setBackgroundSoundVolume(double volume) async {
|
||||
bgVolume = volume;
|
||||
await backgroundSoundAudioPlayer.setVolume(volume / 100);
|
||||
}
|
||||
|
||||
bool handleFadeAtEnd(
|
||||
Duration position,
|
||||
Duration maxDuration,
|
||||
) {
|
||||
var isEnding = maxDuration.inSeconds > 0 &&
|
||||
position.inSeconds > maxDuration.inSeconds - fadeDurationInSeconds;
|
||||
if (isEnding) {
|
||||
_setBgVolumeFadeAtEnd();
|
||||
} else {
|
||||
if (bgVolume != null) {
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
setBackgroundSoundVolume(bgVolume);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return isEnding;
|
||||
}
|
||||
|
||||
void _setBgVolumeFadeAtEnd() {
|
||||
if (backgroundSoundAudioPlayer.volume > 0) {
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
var newVolume = backgroundSoundAudioPlayer.volume - 0.05;
|
||||
if (newVolume > 0) {
|
||||
unawaited(backgroundSoundAudioPlayer.setVolume(newVolume));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void setMediaItem(
|
||||
TrackModel trackModel,
|
||||
TrackFilesModel file, {
|
||||
String? filePath,
|
||||
}) {
|
||||
var item = MediaItem(
|
||||
id: filePath ?? file.path,
|
||||
title: trackModel.title,
|
||||
artist: trackModel.artist?.name,
|
||||
duration: Duration(milliseconds: file.duration),
|
||||
artUri: Uri.parse(
|
||||
trackModel.coverUrl,
|
||||
),
|
||||
extras: {
|
||||
hasBgSoundKey: trackModel.hasBackgroundSound,
|
||||
'trackId': trackModel.id,
|
||||
'fileId': file.id,
|
||||
},
|
||||
);
|
||||
mediaItem.add(item);
|
||||
}
|
||||
|
||||
bool mediaItemHasBGSound() {
|
||||
return mediaItem.value?.extras?[hasBgSoundKey] ?? false;
|
||||
}
|
||||
|
||||
PlaybackState _transformEvent(PlaybackEvent event) {
|
||||
return PlaybackState(
|
||||
controls: [
|
||||
MediaControl.rewind,
|
||||
if (trackAudioPlayer.playing) MediaControl.pause else MediaControl.play,
|
||||
MediaControl.stop,
|
||||
MediaControl.fastForward,
|
||||
],
|
||||
systemActions: const {
|
||||
MediaAction.seek,
|
||||
MediaAction.seekForward,
|
||||
MediaAction.seekBackward,
|
||||
},
|
||||
androidCompactActionIndices: const [0, 1, 3],
|
||||
processingState: const {
|
||||
ProcessingState.idle: AudioProcessingState.idle,
|
||||
ProcessingState.loading: AudioProcessingState.loading,
|
||||
ProcessingState.buffering: AudioProcessingState.buffering,
|
||||
ProcessingState.ready: AudioProcessingState.ready,
|
||||
ProcessingState.completed: AudioProcessingState.completed,
|
||||
}[trackAudioPlayer.processingState]!,
|
||||
playing: trackAudioPlayer.playing,
|
||||
updatePosition: trackAudioPlayer.position,
|
||||
bufferedPosition: trackAudioPlayer.bufferedPosition,
|
||||
speed: trackAudioPlayer.speed,
|
||||
queueIndex: event.currentIndex,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
part 'audio_position_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
void slideAudioPosition(
|
||||
SlideAudioPositionRef ref, {
|
||||
required int duration,
|
||||
}) {
|
||||
final audioPlayer = ref.watch(audioPlayerNotifierProvider);
|
||||
|
||||
audioPlayer.seekValueFromSlider(duration);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
void skipAudio(
|
||||
SkipAudioRef ref, {
|
||||
required SKIP_AUDIO skip,
|
||||
}) {
|
||||
final audioPlayer = ref.watch(audioPlayerNotifierProvider);
|
||||
switch (skip) {
|
||||
case SKIP_AUDIO.SKIP_FORWARD_10:
|
||||
audioPlayer.skipForward10Secs();
|
||||
break;
|
||||
case SKIP_AUDIO.SKIP_BACKWARD_10:
|
||||
audioPlayer.skipBackward10Secs();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final audioPositionAndPlayerStateProvider =
|
||||
StreamProvider<PositionAndPlayerStateState>((ref) {
|
||||
final audioPlayer = ref.watch(audioPlayerNotifierProvider);
|
||||
|
||||
return Rx.combineLatest3<Duration, Duration?, PlayerState,
|
||||
PositionAndPlayerStateState>(
|
||||
audioPlayer.trackAudioPlayer.positionStream,
|
||||
audioPlayer.trackAudioPlayer.durationStream,
|
||||
audioPlayer.trackAudioPlayer.playerStateStream,
|
||||
(position, duration, playerState) =>
|
||||
PositionAndPlayerStateState(playerState, duration, position),
|
||||
);
|
||||
});
|
||||
|
||||
final audioPlaybackStreamProvider = StreamProvider<ProcessingState>((ref) {
|
||||
final audioPlayer = ref.watch(audioPlayerNotifierProvider);
|
||||
|
||||
return audioPlayer.trackAudioPlayer.playbackEventStream
|
||||
.map((event) => event.processingState);
|
||||
});
|
||||
|
||||
//ignore: prefer-match-file-name
|
||||
enum SKIP_AUDIO { SKIP_FORWARD_10, SKIP_BACKWARD_10 }
|
||||
|
||||
class PositionAndPlayerStateState {
|
||||
final PlayerState playerState;
|
||||
final Duration position;
|
||||
final Duration? duration;
|
||||
|
||||
PositionAndPlayerStateState(this.playerState, this.duration, this.position);
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final audioSpeedProvider = ChangeNotifierProvider<AudioSpeedProvider>((ref) {
|
||||
return AudioSpeedProvider(ref);
|
||||
});
|
||||
|
||||
class AudioSpeedProvider extends ChangeNotifier {
|
||||
final Ref ref;
|
||||
final List<String> _speedList = [
|
||||
StringConstants.x06,
|
||||
StringConstants.x07,
|
||||
StringConstants.x08,
|
||||
StringConstants.x09,
|
||||
StringConstants.x1,
|
||||
StringConstants.x125,
|
||||
StringConstants.x15,
|
||||
];
|
||||
AudioSpeedModel audioSpeedModel = AudioSpeedModel();
|
||||
|
||||
AudioSpeedProvider(this.ref);
|
||||
|
||||
void setAudioTrackSpeed() {
|
||||
double speed;
|
||||
String label;
|
||||
var nextIndex = _speedList.indexOf(audioSpeedModel.label) + 1;
|
||||
label =
|
||||
nextIndex >= _speedList.length ? _speedList[0] : _speedList[nextIndex];
|
||||
if (label == StringConstants.x06) {
|
||||
speed = 0.6;
|
||||
} else if (label == StringConstants.x07) {
|
||||
speed = 0.7;
|
||||
} else if (label == StringConstants.x08) {
|
||||
speed = 0.8;
|
||||
} else if (label == StringConstants.x09) {
|
||||
speed = 0.9;
|
||||
} else if (label == StringConstants.x1) {
|
||||
speed = 1;
|
||||
} else if (label == StringConstants.x125) {
|
||||
speed = 1.25;
|
||||
} else if (label == StringConstants.x15) {
|
||||
speed = 1.5;
|
||||
} else {
|
||||
speed = 1;
|
||||
}
|
||||
audioSpeedModel = AudioSpeedModel(label: label, speed: speed);
|
||||
unawaited(
|
||||
ref.read(sharedPreferencesProvider).setString(
|
||||
SharedPreferenceConstants.sessionAudioSpeed,
|
||||
json.encode(
|
||||
audioSpeedModel.toJson(),
|
||||
),
|
||||
),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void getAudioTrackSpeedFromPref() {
|
||||
var audioSpeedFromPref = ref.read(sharedPreferencesProvider).getString(
|
||||
SharedPreferenceConstants.sessionAudioSpeed,
|
||||
);
|
||||
if (audioSpeedFromPref != null) {
|
||||
audioSpeedModel =
|
||||
AudioSpeedModel.fromJson(json.decode(audioSpeedFromPref));
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
53
lib/providers/player/audio_state_provider.dart
Normal file
53
lib/providers/player/audio_state_provider.dart
Normal file
@ -0,0 +1,53 @@
|
||||
import 'package:Medito/main.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../src/audio_pigeon.g.dart';
|
||||
|
||||
class AudioStateProvider implements MeditoAudioServiceCallbackApi {
|
||||
final AudioStateNotifier notifier;
|
||||
|
||||
AudioStateProvider(this.notifier);
|
||||
|
||||
@override
|
||||
void updatePlaybackState(PlaybackState state) {
|
||||
notifier.updatePlaybackState(state);
|
||||
}
|
||||
}
|
||||
|
||||
class AudioStateNotifier extends StateNotifier<PlaybackState> {
|
||||
AudioStateNotifier()
|
||||
: super(PlaybackState(
|
||||
position: 0,
|
||||
isPlaying: false,
|
||||
isBuffering: false,
|
||||
isSeeking: false,
|
||||
isCompleted: false,
|
||||
duration: 0,
|
||||
speed: Speed(speed: 1),
|
||||
volume: 100,
|
||||
track: Track(title: '', description: '', imageUrl: '', artist: ''),
|
||||
));
|
||||
|
||||
void updatePlaybackState(PlaybackState newState) {
|
||||
state = newState;
|
||||
}
|
||||
|
||||
void resetState() {
|
||||
state = PlaybackState(
|
||||
position: 0,
|
||||
isPlaying: false,
|
||||
isBuffering: false,
|
||||
isSeeking: false,
|
||||
isCompleted: false,
|
||||
duration: 0,
|
||||
speed: Speed(speed: 1),
|
||||
volume: 100,
|
||||
track: Track(title: '', description: '', imageUrl: '', artist: ''),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final audioStateProvider =
|
||||
StateNotifierProvider<AudioStateNotifier, PlaybackState>((ref) {
|
||||
return audioStateNotifier;
|
||||
});
|
@ -26,7 +26,7 @@ class AudioDownloaderProvider extends ChangeNotifier {
|
||||
'${trackModel.id}-${file.id}${getAudioFileExtension(file.path)}';
|
||||
try {
|
||||
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
||||
audioDownloadState[fileName] = AUDIO_DOWNLOAD_STATE.DOWNLOADIING;
|
||||
audioDownloadState[fileName] = AUDIO_DOWNLOAD_STATE.DOWNLOADING;
|
||||
await downloadAudio.downloadFile(
|
||||
file.path,
|
||||
name: fileName,
|
||||
@ -67,7 +67,7 @@ class AudioDownloaderProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<String?> getTrackAudio(String fileName) async {
|
||||
Future<String?> getTrackPath(String fileName) async {
|
||||
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
||||
var audioPath = await downloadAudio.getDownloadedFile(fileName);
|
||||
audioDownloadState[fileName] = audioPath != null
|
||||
@ -81,6 +81,6 @@ class AudioDownloaderProvider extends ChangeNotifier {
|
||||
|
||||
enum AUDIO_DOWNLOAD_STATE {
|
||||
DOWNLOAD,
|
||||
DOWNLOADIING,
|
||||
DOWNLOADING,
|
||||
DOWNLOADED,
|
||||
}
|
||||
|
@ -1,10 +1,17 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/utils/utils.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
|
||||
import '../../constants/types/type_constants.dart';
|
||||
import '../../src/audio_pigeon.g.dart';
|
||||
import '../../utils/utils.dart';
|
||||
import '../../utils/workmanager.dart';
|
||||
import '../events/events_provider.dart';
|
||||
import 'download/audio_downloader_provider.dart';
|
||||
|
||||
final _api = MeditoAudioServiceApi();
|
||||
|
||||
final playerProvider =
|
||||
StateNotifierProvider<PlayerProvider, TrackModel?>((ref) {
|
||||
@ -33,84 +40,78 @@ class PlayerProvider extends StateNotifier<TrackModel?> {
|
||||
}
|
||||
});
|
||||
|
||||
final audioPlayerNotifier = ref.read(audioPlayerNotifierProvider);
|
||||
|
||||
state = track;
|
||||
_optionallyLoadBackgroundSound(
|
||||
ref,
|
||||
audioPlayerNotifier,
|
||||
track,
|
||||
track.audio.first.files.first,
|
||||
);
|
||||
await _playTrack(
|
||||
ref,
|
||||
audioPlayerNotifier,
|
||||
track,
|
||||
file,
|
||||
);
|
||||
}
|
||||
|
||||
void _optionallyLoadBackgroundSound(
|
||||
Ref ref,
|
||||
AudioPlayerNotifier audioPlayerNotifier,
|
||||
TrackModel trackModel,
|
||||
TrackFilesModel file,
|
||||
) {
|
||||
var isPlaying = audioPlayerNotifier.trackAudioPlayer.playerState.playing;
|
||||
var currentPlayingFileId = audioPlayerNotifier.currentlyPlayingTrack?.id;
|
||||
|
||||
if (!isPlaying || currentPlayingFileId != file.id) {
|
||||
_optionallyPlayBackgroundSound(
|
||||
ref,
|
||||
audioPlayerNotifier,
|
||||
trackModel.hasBackgroundSound,
|
||||
);
|
||||
}
|
||||
state = track;
|
||||
}
|
||||
|
||||
Future<void> _playTrack(
|
||||
Ref ref,
|
||||
AudioPlayerNotifier audioPlayerNotifier,
|
||||
TrackModel trackModel,
|
||||
TrackFilesModel file,
|
||||
) async {
|
||||
var checkDownloadedFile = ref.read(audioDownloaderProvider).getTrackAudio(
|
||||
await startBackgroundThreadForAudioCompleteEvent(
|
||||
file.id,
|
||||
trackModel.id,
|
||||
file.duration,
|
||||
);
|
||||
|
||||
var downloadPath = await ref.read(audioDownloaderProvider).getTrackPath(
|
||||
_constructFileName(trackModel, file),
|
||||
);
|
||||
var filePath = await checkDownloadedFile;
|
||||
audioPlayerNotifier.setTrackAudio(
|
||||
trackModel,
|
||||
file,
|
||||
filePath: filePath,
|
||||
await _api.playAudio(
|
||||
AudioData(
|
||||
url: downloadPath ?? file.path,
|
||||
track: Track(
|
||||
title: trackModel.title,
|
||||
artist: trackModel.artist?.name ?? '',
|
||||
artistUrl: trackModel.artist?.path ?? '',
|
||||
description: trackModel.description,
|
||||
imageUrl: trackModel.coverUrl,
|
||||
),
|
||||
),
|
||||
);
|
||||
audioPlayerNotifier.currentlyPlayingTrack = file;
|
||||
unawaited(audioPlayerNotifier.play());
|
||||
}
|
||||
|
||||
Future<void> startBackgroundThreadForAudioCompleteEvent(
|
||||
String fileId,
|
||||
String trackModelId,
|
||||
int duration,
|
||||
) async {
|
||||
await Workmanager().initialize(
|
||||
callbackDispatcher,
|
||||
isInDebugMode: true,
|
||||
);
|
||||
|
||||
await Workmanager().registerOneOffTask(
|
||||
audioCompletedTaskKey,
|
||||
audioCompletedTaskKey,
|
||||
initialDelay: Duration(milliseconds: duration),
|
||||
constraints: Constraints(
|
||||
networkType: NetworkType.connected,
|
||||
requiresBatteryNotLow: false,
|
||||
requiresCharging: false,
|
||||
requiresDeviceIdle: false,
|
||||
requiresStorageNotLow: false,
|
||||
),
|
||||
inputData: {
|
||||
TypeConstants.fileIdKey: fileId,
|
||||
TypeConstants.trackIdKey: trackModelId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _cancelBackgroundThreadForAudioCompleteEvent() async {
|
||||
await Workmanager().cancelAll();
|
||||
}
|
||||
|
||||
String _constructFileName(TrackModel trackModel, TrackFilesModel file) =>
|
||||
'${trackModel.id}-${file.id}${getAudioFileExtension(file.path)}';
|
||||
|
||||
void _optionallyPlayBackgroundSound(
|
||||
Ref ref,
|
||||
AudioPlayerNotifier audioPlayerNotifier,
|
||||
bool hasBackgroundSound,
|
||||
) {
|
||||
if (hasBackgroundSound) {
|
||||
final _provider = ref.read(backgroundSoundsNotifierProvider);
|
||||
_provider.getBackgroundSoundFromPref();
|
||||
if (_provider.selectedBgSound != null &&
|
||||
_provider.selectedBgSound?.title != StringConstants.none) {
|
||||
audioPlayerNotifier.setBackgroundAudio(_provider.selectedBgSound!);
|
||||
audioPlayerNotifier.playBackgroundSound();
|
||||
}
|
||||
|
||||
_provider.getVolumeFromPref();
|
||||
audioPlayerNotifier.setBackgroundSoundVolume(_provider.volume);
|
||||
} else {
|
||||
audioPlayerNotifier.pauseBackgroundSound();
|
||||
}
|
||||
}
|
||||
|
||||
void handleAudioStartedEvent(
|
||||
String trackId,
|
||||
String audioFileId,
|
||||
@ -123,19 +124,28 @@ class PlayerProvider extends StateNotifier<TrackModel?> {
|
||||
ref.read(eventsProvider(event: event.toJson()));
|
||||
}
|
||||
|
||||
void handleAudioCompletionEvent(
|
||||
String audioFileId,
|
||||
String trackId,
|
||||
) {
|
||||
var audio = AudioCompletedModel(
|
||||
audioFileId: audioFileId,
|
||||
trackId: trackId,
|
||||
updateStats: true,
|
||||
);
|
||||
var event = EventsModel(
|
||||
name: EventTypes.audioCompleted,
|
||||
payload: audio.toJson(),
|
||||
);
|
||||
ref.read(eventsProvider(event: event.toJson()));
|
||||
Future<void> seekToPosition(int position) async {
|
||||
await _api.seekToPosition(position);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_cancelBackgroundThreadForAudioCompleteEvent();
|
||||
_api.stopAudio();
|
||||
}
|
||||
|
||||
void setSpeed(double speed) {
|
||||
_api.setSpeed(speed);
|
||||
}
|
||||
|
||||
void skip10SecondsForward() {
|
||||
_api.skip10SecondsForward();
|
||||
}
|
||||
|
||||
void skip10SecondsBackward() {
|
||||
_api.skip10SecondsBackward();
|
||||
}
|
||||
|
||||
void playPause() {
|
||||
_api.playPauseAudio();
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,16 @@
|
||||
export 'auth/auth_provider.dart';
|
||||
export 'auth/initialize_user_provider.dart';
|
||||
export 'me/me_provider.dart';
|
||||
export 'home/home_provider.dart';
|
||||
export 'player/audio_player_provider.dart';
|
||||
export 'background_sounds/background_sounds_provider.dart';
|
||||
export 'connectivity/connectivity_provider.dart';
|
||||
export 'device_and_app_info/device_and_app_info_provider.dart';
|
||||
export 'events/app_opened_event_provider.dart';
|
||||
export 'events/events_provider.dart';
|
||||
export 'explore/explore_provider.dart';
|
||||
export 'meditation/download_track_provider.dart';
|
||||
export 'meditation/track_provider.dart';
|
||||
export 'notification/notification_provider.dart';
|
||||
export 'pack/pack_provider.dart';
|
||||
export 'page_view/bottom_padding_provider.dart';
|
||||
export 'player/audio_play_pause_provider.dart';
|
||||
export 'player/audio_position_provider.dart';
|
||||
export 'player/audio_speed_provider.dart';
|
||||
export 'player/audio_state_provider.dart';
|
||||
export 'player/download/audio_downloader_provider.dart';
|
||||
export 'player/player_provider.dart';
|
||||
export 'explore/explore_provider.dart';
|
||||
export 'shared_preference/shared_preference_provider.dart';
|
||||
export 'events/app_opened_event_provider.dart';
|
||||
export 'stats/stats_provider.dart';
|
||||
|
@ -1,11 +1,6 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/routes/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
|
||||
final rootCombineProvider = Provider.family<void, BuildContext>((ref, context) {
|
||||
ref.read(remoteStatsProvider);
|
||||
@ -13,54 +8,4 @@ final rootCombineProvider = Provider.family<void, BuildContext>((ref, context) {
|
||||
ref.read(postLocalStatsProvider);
|
||||
ref.read(deviceAppAndUserInfoProvider);
|
||||
ref.read(audioDownloaderProvider).deleteDownloadedFileFromPreviousVersion();
|
||||
var audioPlayerProvider = ref.read(audioPlayerNotifierProvider);
|
||||
|
||||
var streamEvent = audioPlayerProvider.trackAudioPlayer.playerStateStream
|
||||
.map((event) => event.processingState)
|
||||
.distinct();
|
||||
streamEvent.forEach((element) {
|
||||
if (element == ProcessingState.completed) {
|
||||
_handleAudioCompletion(ref, context);
|
||||
_handleUserNotSignedIn(ref, context);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
void _handleAudioCompletion(Ref ref, BuildContext context) {
|
||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||
final bgSoundProvider = ref.read(backgroundSoundsNotifierProvider);
|
||||
var extras = audioProvider.mediaItem.value?.extras;
|
||||
if (extras != null) {
|
||||
ref.read(playerProvider.notifier).handleAudioCompletionEvent(
|
||||
extras[TypeConstants.fileIdKey],
|
||||
extras[TypeConstants.trackIdKey],
|
||||
);
|
||||
|
||||
audioProvider.seekValueFromSlider(0);
|
||||
audioProvider.pause();
|
||||
audioProvider.setBackgroundSoundVolume(bgSoundProvider.volume);
|
||||
audioProvider.stop();
|
||||
ref.invalidate(packProvider);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.read(audioPlayPauseStateProvider.notifier).state =
|
||||
PLAY_PAUSE_AUDIO.PAUSE;
|
||||
});
|
||||
var currentlyPlayingTrack = ref.read(playerProvider);
|
||||
var endScreen = currentlyPlayingTrack?.endScreen;
|
||||
if (endScreen != null) {
|
||||
context.push(RouteConstants.endScreenPath, extra: endScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _handleUserNotSignedIn(Ref ref, BuildContext context) {
|
||||
var _user =
|
||||
ref.read(authProvider.notifier).userResponse.body as UserTokenModel;
|
||||
if (_user.email == null) {
|
||||
var params = JoinRouteParamsModel(screen: Screen.track);
|
||||
context.push(
|
||||
RouteConstants.joinIntroPath,
|
||||
extra: params,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
@ -78,5 +77,5 @@ class AuthRepositoryImpl extends AuthRepository {
|
||||
|
||||
@riverpod
|
||||
AuthRepository authRepository(AuthRepositoryRef ref) {
|
||||
return AuthRepositoryImpl(ref: ref, client: ref.watch(dioClientProvider));
|
||||
return AuthRepositoryImpl(ref: ref, client: DioApiService());
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
@ -159,7 +158,7 @@ BackgroundSoundsRepositoryImpl backgroundSoundsRepository(
|
||||
BackgroundSoundsRepositoryRef ref,
|
||||
) {
|
||||
return BackgroundSoundsRepositoryImpl(
|
||||
client: ref.watch(dioClientProvider),
|
||||
client: DioApiService(),
|
||||
ref: ref,
|
||||
);
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import 'dart:io';
|
||||
import 'package:Medito/constants/strings/shared_preference_constants.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:Medito/utils/utils.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -115,7 +114,7 @@ class DownloaderRepositoryImpl extends DownloaderRepository {
|
||||
@riverpod
|
||||
DownloaderRepositoryImpl downloaderRepository(DownloaderRepositoryRef ref) {
|
||||
return DownloaderRepositoryImpl(
|
||||
client: ref.watch(dioClientProvider),
|
||||
client: DioApiService(),
|
||||
ref: ref,
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'events_repository.g.dart';
|
||||
@ -35,5 +34,5 @@ class EventsRepositoryImpl extends EventsRepository {
|
||||
|
||||
@riverpod
|
||||
EventsRepository eventsRepository(EventsRepositoryRef ref) {
|
||||
return EventsRepositoryImpl(client: ref.watch(dioClientProvider));
|
||||
return EventsRepositoryImpl(client: DioApiService());
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'explore_repository.g.dart';
|
||||
@ -36,6 +35,6 @@ class ExploreRepositoryImpl extends ExploreRepository {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
ExploreRepositoryImpl exploreRepository(ref) {
|
||||
return ExploreRepositoryImpl(client: ref.watch(dioClientProvider));
|
||||
ExploreRepositoryImpl exploreRepository(ExploreRepositoryRef ref) {
|
||||
return ExploreRepositoryImpl(client: DioApiService());
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
@ -114,5 +113,5 @@ class HomeRepositoryImpl extends HomeRepository {
|
||||
|
||||
@riverpod
|
||||
HomeRepositoryImpl homeRepository(HomeRepositoryRef ref) {
|
||||
return HomeRepositoryImpl(ref: ref, client: ref.watch(dioClientProvider));
|
||||
return HomeRepositoryImpl(ref: ref, client: DioApiService());
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'me_repository.g.dart';
|
||||
@ -25,5 +24,5 @@ class MeRepositoryImpl extends MeRepository {
|
||||
|
||||
@riverpod
|
||||
MeRepository meRepository(MeRepositoryRef ref) {
|
||||
return MeRepositoryImpl(client: ref.watch(dioClientProvider));
|
||||
return MeRepositoryImpl(client: DioApiService());
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'packs_repository.g.dart';
|
||||
@ -35,5 +34,5 @@ class PackRepositoryImpl extends PacksRepository {
|
||||
|
||||
@riverpod
|
||||
PackRepositoryImpl packRepository(PackRepositoryRef ref) {
|
||||
return PackRepositoryImpl(client: ref.watch(dioClientProvider));
|
||||
return PackRepositoryImpl(client: DioApiService());
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ export 'background_sounds/background_sounds_repository.dart';
|
||||
export 'device_and_app_info/device_and_app_info_repository.dart';
|
||||
export 'downloader/downloader_repository.dart';
|
||||
export 'events/events_repository.dart';
|
||||
export 'explore/explore_repository.dart';
|
||||
export 'home/home_repository.dart';
|
||||
export 'me/me_repository.dart';
|
||||
export 'pack/packs_repository.dart';
|
||||
export 'explore/explore_repository.dart';
|
||||
export 'stats/stats_repository.dart';
|
||||
export 'track/track_repository.dart';
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:Medito/utils/stats_utils.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
@ -98,6 +97,6 @@ class StatsRepositoryImpl extends StatsRepository {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
StatsRepository statsRepository(StatsRepositoryRef ref) {
|
||||
return StatsRepositoryImpl(client: ref.watch(dioClientProvider));
|
||||
StatsRepository statsRepository(StatsRepositoryRef _) {
|
||||
return StatsRepositoryImpl(client: DioApiService());
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:Medito/services/network/dio_client_provider.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
@ -97,5 +96,5 @@ class TrackRepositoryImpl extends TrackRepository {
|
||||
|
||||
@riverpod
|
||||
TrackRepository trackRepository(TrackRepositoryRef ref) {
|
||||
return TrackRepositoryImpl(ref: ref, client: ref.watch(dioClientProvider));
|
||||
return TrackRepositoryImpl(ref: ref, client: DioApiService());
|
||||
}
|
||||
|
@ -12,13 +12,13 @@ import 'package:Medito/views/background_sound/background_sound_view.dart';
|
||||
import 'package:Medito/views/bottom_navigation/bottom_navigation_bar_view.dart';
|
||||
import 'package:Medito/views/downloads/downloads_view.dart';
|
||||
import 'package:Medito/views/end_screen/end_screen_view.dart';
|
||||
import 'package:Medito/views/explore/explore_view.dart';
|
||||
import 'package:Medito/views/home/home_view.dart';
|
||||
import 'package:Medito/views/missing_route/missing_route_view.dart';
|
||||
import 'package:Medito/views/notifications/notification_permission_view.dart';
|
||||
import 'package:Medito/views/pack/pack_view.dart';
|
||||
import 'package:Medito/views/player/player_view.dart';
|
||||
import 'package:Medito/views/root/root_page_view.dart';
|
||||
import 'package:Medito/views/explore/explore_view.dart';
|
||||
import 'package:Medito/views/splash_view.dart';
|
||||
import 'package:Medito/views/track/track_view.dart';
|
||||
import 'package:Medito/widgets/widgets.dart';
|
||||
|
@ -1,10 +1,72 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import '../../constants/http/http_constants.dart';
|
||||
|
||||
// ignore: avoid_dynamic_calls
|
||||
class DioApiService {
|
||||
Dio dio;
|
||||
static final DioApiService _instance = DioApiService._internal();
|
||||
late Dio dio;
|
||||
|
||||
DioApiService({required this.dio});
|
||||
factory DioApiService() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
// Private constructor
|
||||
DioApiService._internal() {
|
||||
dio = Dio();
|
||||
dio.options = BaseOptions(
|
||||
connectTimeout: Duration(milliseconds: 30000),
|
||||
baseUrl: HTTPConstants.BASE_URL,
|
||||
headers: {
|
||||
HttpHeaders.accessControlAllowOriginHeader: '*',
|
||||
HttpHeaders.accessControlAllowHeadersHeader:
|
||||
'Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale',
|
||||
HttpHeaders.accessControlAllowCredentialsHeader: 'true',
|
||||
HttpHeaders.accessControlAllowMethodsHeader: 'POST, OPTIONS, HEAD, GET',
|
||||
HttpHeaders.contentTypeHeader: ContentType.json.value,
|
||||
HttpHeaders.refererHeader: 'no-referrer-when-downgrade',
|
||||
HttpHeaders.acceptHeader: '*/*',
|
||||
},
|
||||
);
|
||||
if (kDebugMode) {
|
||||
dio.interceptors.add(LogInterceptor(
|
||||
request: true,
|
||||
responseBody: true,
|
||||
requestBody: true,
|
||||
error: true,
|
||||
));
|
||||
}
|
||||
dio.interceptors.add(
|
||||
InterceptorsWrapper(onError: (e, handler) => _onError(e, handler)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onError(
|
||||
DioException err,
|
||||
ErrorInterceptorHandler handler,
|
||||
) async {
|
||||
await _captureException(err);
|
||||
handler.reject(err);
|
||||
}
|
||||
|
||||
|
||||
Future<void> _captureException(
|
||||
DioException err,
|
||||
) async {
|
||||
await Sentry.captureException(
|
||||
{
|
||||
'error': err.toString(),
|
||||
'endpoint': err.requestOptions.path.toString(),
|
||||
'response': err.response.toString(),
|
||||
'serverMessage': err.message.toString(),
|
||||
},
|
||||
stackTrace: err.stackTrace,
|
||||
);
|
||||
}
|
||||
|
||||
// ignore: avoid-dynamic
|
||||
Future<dynamic> getRequest(
|
||||
|
@ -1,74 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/services/network/dio_api_service.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
final dioClientProvider = Provider<DioApiService>((ref) {
|
||||
var dio = Dio();
|
||||
dio.options = BaseOptions(
|
||||
connectTimeout: Duration(milliseconds: 30000),
|
||||
baseUrl: HTTPConstants.BASE_URL,
|
||||
headers: {
|
||||
HttpHeaders.accessControlAllowOriginHeader: '*',
|
||||
HttpHeaders.accessControlAllowHeadersHeader:
|
||||
'Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale',
|
||||
HttpHeaders.accessControlAllowCredentialsHeader: 'true',
|
||||
HttpHeaders.accessControlAllowMethodsHeader: 'POST, OPTIONS, HEAD, GET',
|
||||
HttpHeaders.contentTypeHeader: ContentType.json.value,
|
||||
HttpHeaders.refererHeader: 'no-referrer-when-downgrade',
|
||||
HttpHeaders.acceptHeader: '*/*',
|
||||
},
|
||||
);
|
||||
if (kDebugMode) {
|
||||
dio.interceptors.add(LogInterceptor(
|
||||
request: true,
|
||||
responseBody: true,
|
||||
requestBody: true,
|
||||
error: true,
|
||||
));
|
||||
}
|
||||
dio.interceptors.add(
|
||||
InterceptorsWrapper(onError: (e, handler) => _onError(e, handler, ref)),
|
||||
);
|
||||
var dioApiService = DioApiService(dio: dio);
|
||||
|
||||
return dioApiService;
|
||||
});
|
||||
|
||||
Future<void> _onError(
|
||||
DioException err,
|
||||
ErrorInterceptorHandler handler,
|
||||
Ref _,
|
||||
) async {
|
||||
await _captureException(err);
|
||||
handler.reject(err);
|
||||
}
|
||||
|
||||
Future<void> _captureException(
|
||||
DioException err,
|
||||
) async {
|
||||
await Sentry.captureException(
|
||||
{
|
||||
'error': err.toString(),
|
||||
'endpoint': err.requestOptions.path.toString(),
|
||||
'response': err.response.toString(),
|
||||
'serverMessage': err.message.toString(),
|
||||
},
|
||||
stackTrace: err.stackTrace,
|
||||
);
|
||||
}
|
||||
|
||||
var assignDioHeadersProvider = FutureProvider<void>((ref) async {
|
||||
var auth = ref.read(authProvider);
|
||||
var deviceInfo = await ref.read(deviceAndAppInfoProvider.future);
|
||||
var headers = ref.read(dioClientProvider).dio.options.headers;
|
||||
var headers = DioApiService().dio.options.headers;
|
||||
|
||||
var user = auth.userResponse.body as UserTokenModel;
|
||||
|
||||
|
@ -80,7 +80,7 @@ Future<bool> updateMinuteCounter(int additionalSecs) async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var current = await _getSecondsListened();
|
||||
var plusOne = current + (additionalSecs);
|
||||
var plusOne = current + additionalSecs;
|
||||
await prefs.setInt(SharedPreferenceConstants.secsListened, plusOne);
|
||||
|
||||
return true;
|
||||
|
@ -110,8 +110,6 @@ Future<void> launchEmailSubmission(
|
||||
|
||||
if (await canLaunchUrl(params)) {
|
||||
await launchUrl(params);
|
||||
} else {
|
||||
var url = params.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
33
lib/utils/workmanager.dart
Normal file
33
lib/utils/workmanager.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
|
||||
import '../constants/types/type_constants.dart';
|
||||
import '../models/events/audio_completed/audio_completed_model.dart';
|
||||
import '../models/events/events_model.dart';
|
||||
import '../repositories/events/events_repository.dart';
|
||||
import '../services/network/dio_api_service.dart';
|
||||
|
||||
const audioCompletedTaskKey = 'com.AVFoundation.medito.audioCompletedTask';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void callbackDispatcher() {
|
||||
Workmanager().executeTask((task, inputData) {
|
||||
switch (task) {
|
||||
case audioCompletedTaskKey:
|
||||
var audio = AudioCompletedModel(
|
||||
audioFileId: inputData?[TypeConstants.fileIdKey],
|
||||
trackId: inputData?[TypeConstants.trackIdKey],
|
||||
updateStats: true,
|
||||
);
|
||||
var event = EventsModel(
|
||||
name: EventTypes.audioCompleted,
|
||||
payload: audio.toJson(),
|
||||
);
|
||||
var eventsRpo = EventsRepositoryImpl(client: DioApiService());
|
||||
if (inputData != null) eventsRpo.trackEvent(event.toJson());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return Future.value(true);
|
||||
});
|
||||
}
|
@ -13,6 +13,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:pin_code_fields/pin_code_fields.dart';
|
||||
|
||||
import '../../providers/me/me_provider.dart';
|
||||
import '../../utils/utils.dart';
|
||||
|
||||
class JoinVerifyOTPView extends ConsumerStatefulWidget {
|
||||
|
@ -5,6 +5,7 @@ import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../providers/background_sounds/background_sounds_notifier.dart';
|
||||
import 'widgets/sound_listtile_widget.dart';
|
||||
import 'widgets/volume_slider_widget.dart';
|
||||
|
||||
@ -23,36 +24,6 @@ class _BackgroundSoundViewState extends ConsumerState<BackgroundSoundView> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController.addListener(_scrollListener);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setInitStateValues();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void deactivate() {
|
||||
final _audioPlayerNotifier = ref.read(audioPlayerNotifierProvider);
|
||||
if (!_audioPlayerNotifier.trackAudioPlayer.playerState.playing) {
|
||||
_audioPlayerNotifier.stopBackgroundSound();
|
||||
}
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
void setInitStateValues() {
|
||||
final _provider = ref.read(backgroundSoundsNotifierProvider);
|
||||
final _audioPlayerNotifier = ref.read(audioPlayerNotifierProvider);
|
||||
if (!_audioPlayerNotifier.backgroundSoundAudioPlayer.playerState.playing) {
|
||||
_provider.getBackgroundSoundFromPref();
|
||||
if (_provider.selectedBgSound != null &&
|
||||
_provider.selectedBgSound?.title != StringConstants.none) {
|
||||
_audioPlayerNotifier.setBackgroundAudio(
|
||||
_provider.selectedBgSound!,
|
||||
);
|
||||
_audioPlayerNotifier.playBackgroundSound();
|
||||
}
|
||||
|
||||
_provider.getVolumeFromPref();
|
||||
_audioPlayerNotifier.setBackgroundSoundVolume(_provider.volume);
|
||||
}
|
||||
}
|
||||
|
||||
void _scrollListener() {
|
||||
@ -151,8 +122,7 @@ class _BackgroundSoundViewState extends ConsumerState<BackgroundSoundView> {
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
height16,
|
||||
height16,
|
||||
height32,
|
||||
]),
|
||||
),
|
||||
],
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
|
||||
import '../../../providers/background_sounds/background_sounds_notifier.dart';
|
||||
|
||||
class SoundListTileWidget extends ConsumerWidget {
|
||||
const SoundListTileWidget({required this.sound}) : super();
|
||||
@ -12,7 +12,6 @@ class SoundListTileWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bgSoundNotifierProvider = ref.watch(backgroundSoundsNotifierProvider);
|
||||
final audioPlayerNotifier = ref.watch(audioPlayerNotifierProvider);
|
||||
var selectedSoundId = bgSoundNotifierProvider.selectedBgSound?.id;
|
||||
var id = selectedSoundId ?? '0';
|
||||
var isSelected = id == sound.id;
|
||||
@ -20,7 +19,6 @@ class SoundListTileWidget extends ConsumerWidget {
|
||||
return InkWell(
|
||||
onTap: () => _handleItemTap(
|
||||
bgSoundNotifierProvider,
|
||||
audioPlayerNotifier,
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
@ -66,17 +64,10 @@ class SoundListTileWidget extends ConsumerWidget {
|
||||
|
||||
void _handleItemTap(
|
||||
BackgroundSoundsNotifier bgSoundNotifierProvider,
|
||||
AudioPlayerNotifier audioPlayerNotifier,
|
||||
) {
|
||||
if (sound.title == StringConstants.none) {
|
||||
bgSoundNotifierProvider.handleOnChangeSound(sound);
|
||||
audioPlayerNotifier.stopBackgroundSound();
|
||||
audioPlayerNotifier.backgroundSoundAudioPlayer.dispose();
|
||||
audioPlayerNotifier.backgroundSoundAudioPlayer = AudioPlayer();
|
||||
} else {
|
||||
audioPlayerNotifier.setBackgroundAudio(sound);
|
||||
audioPlayerNotifier.playBackgroundSound();
|
||||
bgSoundNotifierProvider.handleOnChangeSound(sound);
|
||||
bgSoundNotifierProvider.stopBackgroundSound();
|
||||
}
|
||||
bgSoundNotifierProvider.handleOnChangeSound(sound);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../providers/background_sounds/background_sounds_notifier.dart';
|
||||
import 'background_sound_volume_track_shape_widget.dart';
|
||||
|
||||
class VolumeSliderWidget extends ConsumerWidget {
|
||||
@ -11,14 +11,13 @@ class VolumeSliderWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bgSoundNotifierProvider = ref.watch(backgroundSoundsNotifierProvider);
|
||||
final audioPlayerNotifier = ref.watch(audioPlayerNotifierProvider);
|
||||
var currentVolume = bgSoundNotifierProvider.volume;
|
||||
final currentVolume = bgSoundNotifierProvider.volume;
|
||||
|
||||
return SliderTheme(
|
||||
data: SliderThemeData(
|
||||
trackShape: BackgroundSoundVolumeTrackShapeWidget(
|
||||
leadingTitle: StringConstants.volume,
|
||||
tralingText: currentVolume.toString().split('.').first + '%',
|
||||
tralingText: currentVolume.toString().split('.').first + '%',
|
||||
),
|
||||
overlayShape: RoundSliderOverlayShape(overlayRadius: 0.0),
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 0.0),
|
||||
@ -33,7 +32,6 @@ class VolumeSliderWidget extends ConsumerWidget {
|
||||
inactiveColor: ColorConstants.greyIsTheNewGrey,
|
||||
onChanged: (double newValue) {
|
||||
bgSoundNotifierProvider.handleOnChangeVolume(newValue);
|
||||
audioPlayerNotifier.setBackgroundSoundVolume(newValue);
|
||||
},
|
||||
semanticFormatterCallback: (double newValue) {
|
||||
return '${newValue.round()} ';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/views/home/home_view.dart';
|
||||
import 'package:Medito/views/explore/explore_view.dart';
|
||||
import 'package:Medito/views/home/home_view.dart';
|
||||
import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
@ -13,6 +13,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../providers/background_sounds/background_sounds_notifier.dart';
|
||||
import '../../providers/home/home_provider.dart';
|
||||
|
||||
class DownloadsView extends ConsumerStatefulWidget {
|
||||
@override
|
||||
ConsumerState<DownloadsView> createState() => _DownloadsViewState();
|
||||
@ -148,8 +151,8 @@ class _DownloadsViewState extends ConsumerState<DownloadsView>
|
||||
WidgetRef ref,
|
||||
TrackModel trackModel,
|
||||
) async {
|
||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||
await audioProvider.stop();
|
||||
final bgSoundNotifier = ref.read(backgroundSoundsNotifierProvider);
|
||||
bgSoundNotifier.getVolumeFromPref();
|
||||
await ref.read(playerProvider.notifier).loadSelectedTrack(
|
||||
trackModel: trackModel,
|
||||
file: trackModel.audio.first.files.first,
|
||||
|
@ -2,11 +2,12 @@ import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/widgets/headers/medito_app_bar_small.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'widgets/donation_widget.dart';
|
||||
import 'widgets/feedback_widget.dart';
|
||||
|
||||
class EndScreenView extends StatelessWidget {
|
||||
class EndScreenView extends ConsumerStatefulWidget {
|
||||
final List<EndScreenModel> endScreenModel;
|
||||
|
||||
const EndScreenView({
|
||||
@ -14,6 +15,12 @@ class EndScreenView extends StatelessWidget {
|
||||
required this.endScreenModel,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<EndScreenView> createState() => _EndScreenViewState();
|
||||
}
|
||||
|
||||
class _EndScreenViewState extends ConsumerState<EndScreenView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -35,7 +42,7 @@ class EndScreenView extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: padding16),
|
||||
child: Column(
|
||||
children: endScreenModel.map((e) {
|
||||
children: widget.endScreenModel.map((e) {
|
||||
if (e.name == TypeConstants.donationAskCard) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
@ -58,4 +65,5 @@ class EndScreenView extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@ -18,33 +17,33 @@ class _FeedbackWidgetState extends ConsumerState<FeedbackWidget> {
|
||||
bool isFeedbackAdded = false;
|
||||
|
||||
void _handleFeedbackPress(String feedback) async {
|
||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||
var extras = audioProvider.mediaItem.value?.extras;
|
||||
// final audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||
// var extras = audioProvider.mediaItem.value?.extras;
|
||||
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
var payload = FeedbackTappedModel(
|
||||
trackId: extras?[TypeConstants.trackIdKey] ?? '',
|
||||
audioFileId: extras?[TypeConstants.fileIdKey] ?? '',
|
||||
emoji: feedback,
|
||||
);
|
||||
var event = EventsModel(
|
||||
name: EventTypes.trackFeedback,
|
||||
payload: payload.toJson(),
|
||||
);
|
||||
try {
|
||||
await ref.read(eventsProvider(event: event.toJson()).future);
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
isFeedbackAdded = true;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
isFeedbackAdded = false;
|
||||
});
|
||||
}
|
||||
// var payload = FeedbackTappedModel(
|
||||
// trackId: extras?[TypeConstants.trackIdKey] ?? '',
|
||||
// audioFileId: extras?[TypeConstants.fileIdKey] ?? '',
|
||||
// emoji: feedback,
|
||||
// );
|
||||
// var event = EventsModel(
|
||||
// name: EventTypes.trackFeedback,
|
||||
// payload: payload.toJson(),
|
||||
// );
|
||||
// try {
|
||||
// await ref.read(eventsProvider(event: event.toJson()).future);
|
||||
// setState(() {
|
||||
// isLoading = false;
|
||||
// isFeedbackAdded = true;
|
||||
// });
|
||||
// } catch (e) {
|
||||
// setState(() {
|
||||
// isLoading = false;
|
||||
// isFeedbackAdded = false;
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -6,6 +6,8 @@ import 'package:Medito/views/home/widgets/header_and_announcement_widget.dart';
|
||||
import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../providers/home/home_provider.dart';
|
||||
import 'widgets/editorial/editorial_widget.dart';
|
||||
import 'widgets/quote/quote_widget.dart';
|
||||
import 'widgets/shortcuts/shortcuts_widget.dart';
|
||||
|
@ -5,6 +5,7 @@ import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../../providers/me/me_provider.dart';
|
||||
import '../share_btn/share_btn_widget.dart';
|
||||
|
||||
class DebugBottomSheetWidget extends ConsumerWidget {
|
||||
|
@ -1,13 +1,13 @@
|
||||
import 'package:Medito/constants/colors/color_constants.dart';
|
||||
import 'package:Medito/constants/styles/widget_styles.dart';
|
||||
import 'package:Medito/models/home/editorial/editorial_model.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/routes/routes.dart';
|
||||
import 'package:Medito/utils/utils.dart';
|
||||
import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../providers/home/home_provider.dart';
|
||||
import '../animated_scale_widget.dart';
|
||||
|
||||
class EditorialWidget extends ConsumerWidget {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../providers/home/home_provider.dart';
|
||||
import 'announcement/announcement_widget.dart';
|
||||
import 'header/home_header_widget.dart';
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
import 'package:Medito/constants/colors/color_constants.dart';
|
||||
import 'package:Medito/constants/styles/widget_styles.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../providers/home/home_provider.dart';
|
||||
|
||||
class QuoteWidget extends ConsumerWidget {
|
||||
const QuoteWidget({super.key});
|
||||
|
||||
|
@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:reorderables/reorderables.dart';
|
||||
|
||||
import '../../../../providers/home/home_provider.dart';
|
||||
import '../animated_scale_widget.dart';
|
||||
|
||||
class ShortcutsItemsWidget extends ConsumerStatefulWidget {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../providers/home/home_provider.dart';
|
||||
import 'shortcuts_items_widget.dart';
|
||||
|
||||
class ShortcutsWidget extends ConsumerStatefulWidget {
|
||||
|
@ -1,16 +1,18 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/widgets/headers/medito_app_bar_small.dart';
|
||||
import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:Medito/views/player/widgets/artist_title_widget.dart';
|
||||
import 'package:Medito/views/player/widgets/bottom_actions/bottom_action_widget.dart';
|
||||
import 'package:Medito/views/player/widgets/duration_indicator_widget.dart';
|
||||
import 'package:Medito/views/player/widgets/overlay_cover_image_widget.dart';
|
||||
import 'package:Medito/views/player/widgets/player_buttons/player_buttons_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'widgets/artist_title_widget.dart';
|
||||
import 'widgets/bottom_actions/bottom_action_widget.dart';
|
||||
import 'widgets/duration_indicator_widget.dart';
|
||||
import 'widgets/overlay_cover_image_widget.dart';
|
||||
import 'widgets/player_buttons/player_buttons_widget.dart';
|
||||
import '../../constants/strings/route_constants.dart';
|
||||
import '../../constants/strings/string_constants.dart';
|
||||
import '../../providers/background_sounds/background_sounds_notifier.dart';
|
||||
import '../../widgets/errors/medito_error_widget.dart';
|
||||
import '../../widgets/headers/medito_app_bar_small.dart';
|
||||
|
||||
class PlayerView extends ConsumerStatefulWidget {
|
||||
const PlayerView({
|
||||
@ -21,11 +23,17 @@ class PlayerView extends ConsumerStatefulWidget {
|
||||
ConsumerState<PlayerView> createState() => _PlayerViewState();
|
||||
}
|
||||
|
||||
class _PlayerViewState extends ConsumerState<PlayerView>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
class _PlayerViewState extends ConsumerState<PlayerView> {
|
||||
bool _endScreenOpened = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
var playbackState = ref.watch(audioStateProvider);
|
||||
|
||||
if (playbackState.isCompleted && playbackState.position > 5000) {
|
||||
_resetState();
|
||||
_openEndScreen();
|
||||
}
|
||||
|
||||
var currentlyPlayingTrack = ref.watch(playerProvider);
|
||||
if (currentlyPlayingTrack == null) {
|
||||
@ -34,9 +42,6 @@ class _PlayerViewState extends ConsumerState<PlayerView>
|
||||
message: StringConstants.unableToLoadAudio,
|
||||
);
|
||||
}
|
||||
|
||||
var coverUrl = currentlyPlayingTrack.coverUrl;
|
||||
var artist = currentlyPlayingTrack.artist;
|
||||
var file = currentlyPlayingTrack.audio.first.files.first;
|
||||
|
||||
var size = MediaQuery.of(context).size.width;
|
||||
@ -59,33 +64,46 @@ class _PlayerViewState extends ConsumerState<PlayerView>
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: spacerHeight20),
|
||||
OverlayCoverImageWidget(imageUrl: coverUrl),
|
||||
OverlayCoverImageWidget(imageUrl: playbackState.track.imageUrl),
|
||||
SizedBox(height: spacerHeight48),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||
child: ArtistTitleWidget(
|
||||
trackTitle: currentlyPlayingTrack.title,
|
||||
artistName: artist?.name,
|
||||
artistUrlPath: artist?.path,
|
||||
trackTitle: playbackState.track.title,
|
||||
artistName: playbackState.track.artist,
|
||||
artistUrlPath: playbackState.track.artistUrl,
|
||||
isPlayerScreen: true,
|
||||
),
|
||||
),
|
||||
DurationIndicatorWidget(
|
||||
file: file,
|
||||
trackId: currentlyPlayingTrack.id,
|
||||
totalDuration: playbackState.duration,
|
||||
currentPosition: playbackState.position,
|
||||
onSeekEnd: (value) {
|
||||
ref.read(playerProvider.notifier).seekToPosition(value);
|
||||
},
|
||||
),
|
||||
SizedBox(height: spacerHeight24),
|
||||
Transform.translate(
|
||||
offset: Offset(0, -10),
|
||||
child: PlayerButtonsWidget(
|
||||
file: file,
|
||||
trackModel: currentlyPlayingTrack,
|
||||
isPlaying: playbackState.isPlaying,
|
||||
onPlayPause: () =>
|
||||
ref.read(playerProvider.notifier).playPause(),
|
||||
onSkip10SecondsBackward: () => ref
|
||||
.read(playerProvider.notifier)
|
||||
.skip10SecondsBackward(),
|
||||
onSkip10SecondsForward: () => ref
|
||||
.read(playerProvider.notifier)
|
||||
.skip10SecondsForward(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: spacerHeight24),
|
||||
BottomActionWidget(
|
||||
trackModel: currentlyPlayingTrack,
|
||||
file: file,
|
||||
onSpeedChanged: (speed) =>
|
||||
ref.read(playerProvider.notifier).setSpeed(speed),
|
||||
isBackgroundSoundSelected: _isBackgroundSoundSelected(),
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
],
|
||||
@ -96,15 +114,40 @@ class _PlayerViewState extends ConsumerState<PlayerView>
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> _handleClose() async {
|
||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||
await audioProvider.stop();
|
||||
bool _isBackgroundSoundSelected() {
|
||||
var bgSoundNotifier = ref.read(backgroundSoundsNotifierProvider);
|
||||
|
||||
return bgSoundNotifier.selectedBgSound != null &&
|
||||
bgSoundNotifier.selectedBgSound?.title != StringConstants.none;
|
||||
}
|
||||
|
||||
Future<bool> _handleClose() async {
|
||||
_resetState();
|
||||
context.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
void _resetState() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.read(audioStateProvider.notifier).resetState();
|
||||
ref.read(playerProvider.notifier).stop();
|
||||
});
|
||||
}
|
||||
|
||||
void _openEndScreen() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!_endScreenOpened) {
|
||||
var currentlyPlayingTrack = ref.read(playerProvider);
|
||||
var endScreen = currentlyPlayingTrack?.endScreen;
|
||||
if (endScreen != null) {
|
||||
context.pushReplacement(
|
||||
RouteConstants.endScreenPath,
|
||||
extra: endScreen,
|
||||
);
|
||||
}
|
||||
_endScreenOpened = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/routes/routes.dart';
|
||||
import 'package:Medito/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:marquee/marquee.dart';
|
||||
@ -17,7 +18,7 @@ class ArtistTitleWidget extends ConsumerWidget {
|
||||
this.titleHeight = 35,
|
||||
});
|
||||
|
||||
final String trackTitle;
|
||||
final String? trackTitle;
|
||||
final String? artistName, artistUrlPath;
|
||||
final double trackTitleFontSize;
|
||||
final double artistNameFontSize;
|
||||
@ -31,16 +32,20 @@ class ArtistTitleWidget extends ConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_title(context),
|
||||
if (artistName != null) _subtitle(context),
|
||||
if (artistName.isNotNullAndNotEmpty()) _subtitle(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _title(BuildContext context) {
|
||||
if (trackTitle?.isEmpty == true || trackTitle == null) {
|
||||
return SizedBox(height: titleHeight);
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
height: titleHeight,
|
||||
child: Marquee(
|
||||
text: trackTitle,
|
||||
text: trackTitle ?? 'Title',
|
||||
style: Theme.of(context).primaryTextTheme.headlineMedium?.copyWith(
|
||||
fontFamily: SourceSerif,
|
||||
color: ColorConstants.walterWhite,
|
||||
|
@ -1,10 +1,10 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/views/player/widgets/bottom_actions/widgets/audio_download_widget.dart';
|
||||
import 'package:Medito/views/player/widgets/bottom_actions/widgets/audio_speed_widget.dart';
|
||||
import 'package:Medito/views/player/widgets/bottom_actions/widgets/bg_sound_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'widgets/audio_download_widget.dart';
|
||||
import 'widgets/bg_sound_widget.dart';
|
||||
import '../../../../models/track/track_model.dart';
|
||||
import 'widgets/mark_favourite_widget.dart';
|
||||
|
||||
class BottomActionWidget extends StatelessWidget {
|
||||
@ -12,10 +12,14 @@ class BottomActionWidget extends StatelessWidget {
|
||||
super.key,
|
||||
required this.trackModel,
|
||||
required this.file,
|
||||
required this.isBackgroundSoundSelected,
|
||||
required this.onSpeedChanged,
|
||||
});
|
||||
|
||||
final bool isBackgroundSoundSelected;
|
||||
final TrackModel trackModel;
|
||||
final TrackFilesModel file;
|
||||
final Function(double) onSpeedChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -35,18 +39,16 @@ class BottomActionWidget extends StatelessWidget {
|
||||
if (trackModel.hasBackgroundSound)
|
||||
Expanded(
|
||||
child: BgSoundWidget(
|
||||
isBackgroundSoundSelected: isBackgroundSoundSelected,
|
||||
trackModel: trackModel,
|
||||
file: file,
|
||||
),
|
||||
),
|
||||
if (trackModel.hasBackgroundSound) width8,
|
||||
Expanded(child: AudioSpeedWidget()),
|
||||
Expanded(child: AudioSpeedWidget(onSpeedChanged: onSpeedChanged)),
|
||||
width8,
|
||||
Expanded(
|
||||
child: MarkFavouriteWidget(
|
||||
trackModel: trackModel,
|
||||
file: file,
|
||||
),
|
||||
child: MarkFavouriteWidget(trackModel: trackModel, file: file),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -32,7 +32,7 @@ class AudioDownloadWidget extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
} else if (downloadAudioProvider.audioDownloadState[downloadFileKey] ==
|
||||
AUDIO_DOWNLOAD_STATE.DOWNLOADIING) {
|
||||
AUDIO_DOWNLOAD_STATE.DOWNLOADING) {
|
||||
return showDownloadProgress(downloadAudioProvider, downloadFileKey);
|
||||
} else {
|
||||
return IconButton(
|
||||
|
@ -1,45 +1,44 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class AudioSpeedWidget extends ConsumerStatefulWidget {
|
||||
const AudioSpeedWidget({super.key});
|
||||
const AudioSpeedWidget({super.key, required this.onSpeedChanged});
|
||||
|
||||
final Function(double) onSpeedChanged;
|
||||
|
||||
@override
|
||||
ConsumerState<AudioSpeedWidget> createState() => _AudioSpeedComponentState();
|
||||
}
|
||||
|
||||
class _AudioSpeedComponentState extends ConsumerState<AudioSpeedWidget> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final _provider = ref.read(audioSpeedProvider);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_provider.getAudioTrackSpeedFromPref();
|
||||
ref
|
||||
.read(audioPlayerNotifierProvider)
|
||||
.setTrackAudioSpeed(_provider.audioSpeedModel.speed);
|
||||
});
|
||||
}
|
||||
String _label = 'x1';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _provider = ref.watch(audioSpeedProvider);
|
||||
var audioSpeedModel = _provider.audioSpeedModel;
|
||||
var textColor = audioSpeedModel.label != StringConstants.x1
|
||||
var textColor = _label != StringConstants.x1
|
||||
? ColorConstants.lightPurple
|
||||
: ColorConstants.walterWhite;
|
||||
ColorConstants.walterWhite;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_provider.setAudioTrackSpeed();
|
||||
ref
|
||||
.read(audioPlayerNotifierProvider)
|
||||
.setTrackAudioSpeed(_provider.audioSpeedModel.speed);
|
||||
if (_label == StringConstants.x06) {
|
||||
_label = StringConstants.x07;
|
||||
} else if (_label == StringConstants.x07) {
|
||||
_label = StringConstants.x08;
|
||||
} else if (_label == StringConstants.x08) {
|
||||
_label = StringConstants.x09;
|
||||
} else if (_label == StringConstants.x09) {
|
||||
_label = StringConstants.x1;
|
||||
} else if (_label == StringConstants.x1) {
|
||||
_label = StringConstants.x06;
|
||||
}
|
||||
|
||||
widget.onSpeedChanged(_label.toDouble);
|
||||
},
|
||||
child: Text(
|
||||
audioSpeedModel.label,
|
||||
_label,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
@ -49,3 +48,7 @@ class _AudioSpeedComponentState extends ConsumerState<AudioSpeedWidget> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on String {
|
||||
double get toDouble => double.parse(substring(1));
|
||||
}
|
||||
|
@ -1,27 +1,24 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../../../models/track/track_model.dart';
|
||||
|
||||
class BgSoundWidget extends ConsumerWidget {
|
||||
const BgSoundWidget({
|
||||
super.key,
|
||||
required this.trackModel,
|
||||
required this.file,
|
||||
required this.isBackgroundSoundSelected,
|
||||
});
|
||||
|
||||
final TrackModel trackModel;
|
||||
final TrackFilesModel file;
|
||||
final bool isBackgroundSoundSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedBgSound =
|
||||
ref.watch(backgroundSoundsNotifierProvider).selectedBgSound;
|
||||
var checkIsBgSoundSelected = selectedBgSound != null &&
|
||||
selectedBgSound.title != StringConstants.none;
|
||||
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
context.push(
|
||||
@ -31,7 +28,7 @@ class BgSoundWidget extends ConsumerWidget {
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.music_note,
|
||||
color: checkIsBgSoundSelected
|
||||
color: isBackgroundSoundSelected
|
||||
? ColorConstants.lightPurple
|
||||
: ColorConstants.walterWhite,
|
||||
),
|
||||
|
@ -1,8 +1,4 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/utils/duration_extensions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@ -10,12 +6,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
class DurationIndicatorWidget extends ConsumerStatefulWidget {
|
||||
const DurationIndicatorWidget({
|
||||
super.key,
|
||||
required this.file,
|
||||
required this.trackId,
|
||||
required this.totalDuration,
|
||||
required this.currentPosition,
|
||||
required this.onSeekEnd,
|
||||
});
|
||||
|
||||
final TrackFilesModel file;
|
||||
final String trackId;
|
||||
final int totalDuration;
|
||||
final int currentPosition;
|
||||
final void Function(int) onSeekEnd;
|
||||
|
||||
@override
|
||||
ConsumerState<DurationIndicatorWidget> createState() =>
|
||||
@ -24,61 +22,23 @@ class DurationIndicatorWidget extends ConsumerStatefulWidget {
|
||||
|
||||
class _DurationIndicatorWidgetState
|
||||
extends ConsumerState<DurationIndicatorWidget> {
|
||||
final _minSeconds = 0.0;
|
||||
double? _dragSeekbarValue;
|
||||
double _maxDuration = 0.0;
|
||||
bool _draggingSeekbar = false;
|
||||
bool _isSeekbarBeingDragged = false;
|
||||
double _dragSeekbarValue = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final audioPositionAndPlayerState =
|
||||
ref.watch(audioPositionAndPlayerStateProvider);
|
||||
final audioPlayerNotifier = ref.watch(audioPlayerNotifierProvider);
|
||||
|
||||
_maxDuration = widget.file.duration.toDouble();
|
||||
|
||||
return audioPositionAndPlayerState.when(
|
||||
data: (data) {
|
||||
final value = min(
|
||||
_dragSeekbarValue ?? data.position.inMilliseconds,
|
||||
_maxDuration,
|
||||
);
|
||||
if (_dragSeekbarValue != null && !_draggingSeekbar) {
|
||||
_dragSeekbarValue = null;
|
||||
}
|
||||
audioPlayerNotifier.handleFadeAtEnd(
|
||||
data.position,
|
||||
Duration(milliseconds: _maxDuration.round()),
|
||||
);
|
||||
|
||||
return _durationBar(context, ref, value, data);
|
||||
},
|
||||
error: (error, stackTrace) => SizedBox(),
|
||||
loading: () => SizedBox(),
|
||||
return _durationBar(
|
||||
context,
|
||||
// 99 is to avoid rounding errors which would result in the current
|
||||
// position being larger that the total duration
|
||||
widget.totalDuration / 99,
|
||||
widget.currentPosition / 100,
|
||||
);
|
||||
}
|
||||
|
||||
void onChangeEnd(
|
||||
WidgetRef ref,
|
||||
PositionAndPlayerStateState data,
|
||||
double val,
|
||||
) {
|
||||
ref.read(slideAudioPositionProvider(
|
||||
duration: val.round(),
|
||||
));
|
||||
_draggingSeekbar = false;
|
||||
ref.read(audioPlayerNotifierProvider).handleFadeAtEnd(
|
||||
data.position,
|
||||
Duration(milliseconds: _maxDuration.round()),
|
||||
);
|
||||
}
|
||||
|
||||
Padding _durationBar(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
num currentDuration,
|
||||
PositionAndPlayerStateState data,
|
||||
) {
|
||||
Padding _durationBar(BuildContext context,
|
||||
double totalDuration,
|
||||
double currentDuration,) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 32, right: 32, top: 0, bottom: 0),
|
||||
child: Column(
|
||||
@ -92,28 +52,27 @@ class _DurationIndicatorWidgetState
|
||||
),
|
||||
),
|
||||
child: Slider(
|
||||
min: _minSeconds,
|
||||
min: 0.0,
|
||||
max: totalDuration > 0.0 ? totalDuration : 1.0,
|
||||
activeColor: ColorConstants.walterWhite,
|
||||
inactiveColor: ColorConstants.onyx,
|
||||
max: _maxDuration,
|
||||
value: currentDuration.toDouble(),
|
||||
value:
|
||||
_isSeekbarBeingDragged ? _dragSeekbarValue : currentDuration,
|
||||
onChanged: (val) {
|
||||
if (!_draggingSeekbar) {
|
||||
_draggingSeekbar = true;
|
||||
if (!_isSeekbarBeingDragged) {
|
||||
_isSeekbarBeingDragged = true;
|
||||
}
|
||||
setState(() {
|
||||
_dragSeekbarValue = val;
|
||||
});
|
||||
},
|
||||
onChangeEnd: (val) => onChangeEnd(ref, data, val),
|
||||
onChangeEnd: _onChangeEnd,
|
||||
),
|
||||
),
|
||||
Transform.translate(
|
||||
offset: Offset(0, -14),
|
||||
child: _durationLabels(
|
||||
context,
|
||||
currentDuration.round(),
|
||||
_maxDuration.round(),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -121,37 +80,42 @@ class _DurationIndicatorWidgetState
|
||||
);
|
||||
}
|
||||
|
||||
Row _durationLabels(
|
||||
BuildContext context,
|
||||
int position,
|
||||
int duration,
|
||||
) {
|
||||
void _onChangeEnd(double val) {
|
||||
_isSeekbarBeingDragged = false;
|
||||
widget.onSeekEnd((val * 100).toInt());
|
||||
}
|
||||
|
||||
Row _durationLabels(BuildContext context,) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_durationLabel(
|
||||
context,
|
||||
Duration(milliseconds: position).toMinutesSeconds(),
|
||||
Duration(milliseconds: widget.currentPosition.round())
|
||||
.toMinutesSeconds(),
|
||||
),
|
||||
_durationLabel(
|
||||
context,
|
||||
Duration(milliseconds: duration).toMinutesSeconds(),
|
||||
Duration(milliseconds: widget.totalDuration.round())
|
||||
.toMinutesSeconds(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Text _durationLabel(
|
||||
BuildContext context,
|
||||
String label,
|
||||
) {
|
||||
Text _durationLabel(BuildContext context,
|
||||
String label,) {
|
||||
return Text(
|
||||
label,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: ColorConstants.graphite,
|
||||
fontFamily: DmMono,
|
||||
fontSize: 12,
|
||||
),
|
||||
style: Theme
|
||||
.of(context)
|
||||
.textTheme
|
||||
.titleSmall
|
||||
?.copyWith(
|
||||
color: ColorConstants.graphite,
|
||||
fontFamily: DmMono,
|
||||
fontSize: 12,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -182,19 +146,18 @@ class CustomTrackShape extends RoundedRectSliderTrackShape {
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(
|
||||
PaintingContext context,
|
||||
Offset offset, {
|
||||
required RenderBox parentBox,
|
||||
required SliderThemeData sliderTheme,
|
||||
required Animation<double> enableAnimation,
|
||||
required TextDirection textDirection,
|
||||
required Offset thumbCenter,
|
||||
Offset? secondaryOffset,
|
||||
bool isDiscrete = false,
|
||||
bool isEnabled = false,
|
||||
double additionalActiveTrackHeight = 0,
|
||||
}) {
|
||||
void paint(PaintingContext context,
|
||||
Offset offset, {
|
||||
required RenderBox parentBox,
|
||||
required SliderThemeData sliderTheme,
|
||||
required Animation<double> enableAnimation,
|
||||
required TextDirection textDirection,
|
||||
required Offset thumbCenter,
|
||||
Offset? secondaryOffset,
|
||||
bool isDiscrete = false,
|
||||
bool isEnabled = false,
|
||||
double additionalActiveTrackHeight = 0,
|
||||
}) {
|
||||
super.paint(
|
||||
context,
|
||||
offset,
|
||||
|
@ -1,51 +1,40 @@
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class PlayPauseButtonWidget extends ConsumerWidget {
|
||||
const PlayPauseButtonWidget({super.key, this.iconSize = 72});
|
||||
const PlayPauseButtonWidget({
|
||||
super.key,
|
||||
this.iconSize = 72,
|
||||
required this.isPlaying, required this.onPlayPause,
|
||||
});
|
||||
|
||||
final double iconSize;
|
||||
final bool isPlaying;
|
||||
final Function() onPlayPause;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final provider = ref.watch(audioPlayerNotifierProvider);
|
||||
|
||||
return StreamBuilder<bool>(
|
||||
stream: provider.playbackState.map((state) => state.playing).distinct(),
|
||||
builder: (context, snapshot) {
|
||||
final playing = snapshot.data ?? false;
|
||||
|
||||
return InkWell(
|
||||
onTap: () => _handleTap(ref, playing),
|
||||
borderRadius: BorderRadius.circular(iconSize / 2),
|
||||
child: AnimatedCrossFade(
|
||||
firstChild: Icon(
|
||||
Icons.play_circle_fill,
|
||||
size: iconSize,
|
||||
color: ColorConstants.walterWhite,
|
||||
),
|
||||
secondChild: Icon(
|
||||
Icons.pause_circle_outlined,
|
||||
size: iconSize,
|
||||
color: ColorConstants.walterWhite,
|
||||
),
|
||||
crossFadeState:
|
||||
playing ? CrossFadeState.showSecond : CrossFadeState.showFirst,
|
||||
duration: Duration(milliseconds: 500),
|
||||
),
|
||||
);
|
||||
},
|
||||
return InkWell(
|
||||
onTap: onPlayPause,
|
||||
borderRadius: BorderRadius.circular(iconSize / 2),
|
||||
child: AnimatedCrossFade(
|
||||
firstChild: Icon(
|
||||
Icons.play_circle_fill,
|
||||
size: iconSize,
|
||||
color: ColorConstants.walterWhite,
|
||||
),
|
||||
secondChild: Icon(
|
||||
Icons.pause_circle_outlined,
|
||||
size: iconSize,
|
||||
color: ColorConstants.walterWhite,
|
||||
),
|
||||
crossFadeState:
|
||||
isPlaying ? CrossFadeState.showSecond : CrossFadeState.showFirst,
|
||||
duration: Duration(milliseconds: 250),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleTap(WidgetRef ref, bool isPlaying) {
|
||||
final provider = ref.read(audioPlayerNotifierProvider);
|
||||
if (isPlaying) {
|
||||
provider.pause();
|
||||
} else {
|
||||
provider.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@ -7,13 +5,17 @@ import 'play_pause_button_widget.dart';
|
||||
|
||||
class PlayerButtonsWidget extends ConsumerWidget {
|
||||
const PlayerButtonsWidget({
|
||||
required this.onSkip10SecondsBackward,
|
||||
required this.onSkip10SecondsForward,
|
||||
required this.isPlaying,
|
||||
super.key,
|
||||
required this.trackModel,
|
||||
required this.file,
|
||||
required this.onPlayPause,
|
||||
});
|
||||
|
||||
final TrackFilesModel file;
|
||||
final TrackModel trackModel;
|
||||
final Function() onSkip10SecondsBackward;
|
||||
final Function() onSkip10SecondsForward;
|
||||
final bool isPlaying;
|
||||
final Function() onPlayPause;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@ -21,19 +23,21 @@ class PlayerButtonsWidget extends ConsumerWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_rewindButton(ref),
|
||||
_rewindButton(),
|
||||
SizedBox(width: 32),
|
||||
PlayPauseButtonWidget(),
|
||||
PlayPauseButtonWidget(
|
||||
isPlaying: isPlaying,
|
||||
onPlayPause: onPlayPause,
|
||||
),
|
||||
SizedBox(width: 32),
|
||||
_forwardButton(ref),
|
||||
_forwardButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
IconButton _rewindButton(WidgetRef ref) {
|
||||
IconButton _rewindButton() {
|
||||
return IconButton(
|
||||
onPressed: () =>
|
||||
_handleForwardAndRewind(ref, SKIP_AUDIO.SKIP_BACKWARD_10),
|
||||
onPressed: onSkip10SecondsBackward,
|
||||
icon: Icon(
|
||||
Icons.replay_10_rounded,
|
||||
size: 40,
|
||||
@ -41,30 +45,13 @@ class PlayerButtonsWidget extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
IconButton _forwardButton(WidgetRef ref) {
|
||||
IconButton _forwardButton() {
|
||||
return IconButton(
|
||||
onPressed: () => _handleForwardAndRewind(ref, SKIP_AUDIO.SKIP_FORWARD_10),
|
||||
onPressed: onSkip10SecondsForward,
|
||||
icon: Icon(
|
||||
Icons.forward_10_rounded,
|
||||
size: 40,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleForwardAndRewind(WidgetRef ref, SKIP_AUDIO skip) {
|
||||
var audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||
final audioPositionAndPlayerState =
|
||||
ref.read(audioPositionAndPlayerStateProvider);
|
||||
|
||||
var maxDuration = audioProvider.mediaItem.value?.duration ?? Duration();
|
||||
|
||||
ref.read(
|
||||
skipAudioProvider(skip: skip),
|
||||
);
|
||||
|
||||
audioProvider.handleFadeAtEnd(
|
||||
audioPositionAndPlayerState.value?.position ?? Duration(),
|
||||
maxDuration,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../providers/auth/initialize_user_provider.dart';
|
||||
|
||||
class SplashView extends ConsumerStatefulWidget {
|
||||
const SplashView({super.key});
|
||||
|
||||
|
@ -26,6 +26,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../providers/background_sounds/background_sounds_notifier.dart';
|
||||
|
||||
class TrackView extends ConsumerStatefulWidget {
|
||||
final String id;
|
||||
|
||||
@ -90,6 +92,8 @@ class _TrackViewState extends ConsumerState<TrackView>
|
||||
TrackModel trackModel,
|
||||
TrackFilesModel file,
|
||||
) async {
|
||||
final bgSoundNotifier = ref.read(backgroundSoundsNotifierProvider);
|
||||
bgSoundNotifier.getVolumeFromPref();
|
||||
await ref.read(playerProvider.notifier).loadSelectedTrack(
|
||||
trackModel: trackModel,
|
||||
file: file,
|
||||
|
@ -51,7 +51,7 @@ class MeditoAppBarSmall extends StatelessWidget implements PreferredSizeWidget {
|
||||
Widget getTitleWidget(BuildContext context) {
|
||||
return titleWidget == null
|
||||
? Text(title ?? '', style: Theme.of(context).textTheme.displayLarge)
|
||||
: Row(children: [titleWidget ?? Container()]);
|
||||
: Row(children: [titleWidget!]);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -9,14 +9,14 @@ export 'markdown_widget.dart';
|
||||
export 'network_image_widget.dart';
|
||||
export 'pack_card_widget.dart';
|
||||
export 'shimmers/background_sounds_shimmer_widget.dart';
|
||||
export 'shimmers/explore_initial_page_shimmer_widget.dart';
|
||||
export 'shimmers/explore_result_shimmer_widget.dart';
|
||||
export 'shimmers/folder_shimmer_widget.dart';
|
||||
export 'shimmers/home/editorial_shimmer_widget.dart';
|
||||
export 'shimmers/home/header_and_announcement_shimmer_widget.dart';
|
||||
export 'shimmers/home/home_shimmer_widget.dart';
|
||||
export 'shimmers/home/quote_shimmer_widget.dart';
|
||||
export 'shimmers/home/shortcuts_shimmer_widget.dart';
|
||||
export 'shimmers/home/tiles_shimmer_widget.dart';
|
||||
export 'shimmers/home/editorial_shimmer_widget.dart';
|
||||
export 'shimmers/explore_initial_page_shimmer_widget.dart';
|
||||
export 'shimmers/explore_result_shimmer_widget.dart';
|
||||
export 'shimmers/track_shimmer_widget.dart';
|
||||
export 'snackbar_widget.dart';
|
||||
|
1
linux/flutter/ephemeral/.plugin_symlinks/device_info_plus
Symbolic link
1
linux/flutter/ephemeral/.plugin_symlinks/device_info_plus
Symbolic link
@ -0,0 +1 @@
|
||||
/Users/mike/.pub-cache/hosted/pub.dev/device_info_plus-9.1.1/
|
1
linux/flutter/ephemeral/.plugin_symlinks/package_info_plus
Symbolic link
1
linux/flutter/ephemeral/.plugin_symlinks/package_info_plus
Symbolic link
@ -0,0 +1 @@
|
||||
/Users/mike/.pub-cache/hosted/pub.dev/package_info_plus-4.2.0/
|
1
linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux
Symbolic link
1
linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux
Symbolic link
@ -0,0 +1 @@
|
||||
/Users/mike/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/
|
1
linux/flutter/ephemeral/.plugin_symlinks/sentry_flutter
Symbolic link
1
linux/flutter/ephemeral/.plugin_symlinks/sentry_flutter
Symbolic link
@ -0,0 +1 @@
|
||||
/Users/mike/.pub-cache/hosted/pub.dev/sentry_flutter-7.13.2/
|
1
linux/flutter/ephemeral/.plugin_symlinks/share_plus
Symbolic link
1
linux/flutter/ephemeral/.plugin_symlinks/share_plus
Symbolic link
@ -0,0 +1 @@
|
||||
/Users/mike/.pub-cache/hosted/pub.dev/share_plus-7.2.1/
|
@ -0,0 +1 @@
|
||||
/Users/mike/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/
|
1
linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux
Symbolic link
1
linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux
Symbolic link
@ -0,0 +1 @@
|
||||
/Users/mike/.pub-cache/hosted/pub.dev/url_launcher_linux-3.1.0/
|
19
linux/flutter/generated_plugin_registrant.cc
Normal file
19
linux/flutter/generated_plugin_registrant.cc
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <sentry_flutter/sentry_flutter_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) sentry_flutter_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin");
|
||||
sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
}
|
15
linux/flutter/generated_plugin_registrant.h
Normal file
15
linux/flutter/generated_plugin_registrant.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||
#define GENERATED_PLUGIN_REGISTRANT_
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
|
||||
// Registers Flutter plugins.
|
||||
void fl_register_plugins(FlPluginRegistry* registry);
|
||||
|
||||
#endif // GENERATED_PLUGIN_REGISTRANT_
|
25
linux/flutter/generated_plugins.cmake
Normal file
25
linux/flutter/generated_plugins.cmake
Normal file
@ -0,0 +1,25 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
sentry_flutter
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
34
macos/Flutter/GeneratedPluginRegistrant.swift
Normal file
34
macos/Flutter/GeneratedPluginRegistrant.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import connectivity_macos
|
||||
import device_info_plus
|
||||
import firebase_core
|
||||
import firebase_messaging
|
||||
import flutter_local_notifications
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import sentry_flutter
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
import sqflite
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
}
|
11
macos/Flutter/ephemeral/Flutter-Generated.xcconfig
Normal file
11
macos/Flutter/ephemeral/Flutter-Generated.xcconfig
Normal file
@ -0,0 +1,11 @@
|
||||
// This is a generated file; do not edit or check into version control.
|
||||
FLUTTER_ROOT=/Users/mike/flutter
|
||||
FLUTTER_APPLICATION_PATH=/Users/mike/Projects/meditoapp
|
||||
COCOAPODS_PARALLEL_CODE_SIGN=true
|
||||
FLUTTER_BUILD_DIR=build
|
||||
FLUTTER_BUILD_NAME=3.0.11
|
||||
FLUTTER_BUILD_NUMBER=30011
|
||||
DART_OBFUSCATION=false
|
||||
TRACK_WIDGET_CREATION=true
|
||||
TREE_SHAKE_ICONS=false
|
||||
PACKAGE_CONFIG=.dart_tool/package_config.json
|
12
macos/Flutter/ephemeral/flutter_export_environment.sh
Executable file
12
macos/Flutter/ephemeral/flutter_export_environment.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
export "FLUTTER_ROOT=/Users/mike/flutter"
|
||||
export "FLUTTER_APPLICATION_PATH=/Users/mike/Projects/meditoapp"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=3.0.11"
|
||||
export "FLUTTER_BUILD_NUMBER=30011"
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=.dart_tool/package_config.json"
|
43
macos/Podfile
Normal file
43
macos/Podfile
Normal file
@ -0,0 +1,43 @@
|
||||
platform :osx, '10.14'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
def flutter_root
|
||||
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
|
||||
unless File.exist?(generated_xcode_build_settings_path)
|
||||
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
|
||||
end
|
||||
|
||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||
return matches[1].strip if matches
|
||||
end
|
||||
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
|
||||
end
|
||||
|
||||
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||
|
||||
flutter_macos_podfile_setup
|
||||
|
||||
target 'Runner' do
|
||||
use_frameworks!
|
||||
use_modular_headers!
|
||||
|
||||
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
||||
target 'RunnerTests' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_macos_build_settings(target)
|
||||
end
|
||||
end
|
121
pigeon_conf.dart
Normal file
121
pigeon_conf.dart
Normal file
@ -0,0 +1,121 @@
|
||||
import 'package:pigeon/pigeon.dart';
|
||||
|
||||
// to build the classes: flutter pub run pigeon --input pigeon_conf.dart
|
||||
|
||||
// #docregion config
|
||||
@ConfigurePigeon(PigeonOptions(
|
||||
dartOut: 'lib/src/audio_pigeon.g.dart',
|
||||
dartOptions: DartOptions(),
|
||||
kotlinOut:
|
||||
'android/app/src/main/kotlin/com/meditofoundation/medito/AudioPigeon.g.kt',
|
||||
kotlinOptions: KotlinOptions(),
|
||||
// swiftOut: 'ios/Runner/Messages.g.swift',
|
||||
// swiftOptions: SwiftOptions(),
|
||||
))
|
||||
// #enddocregion config
|
||||
|
||||
// #docregion host-definitions
|
||||
//ignore:prefer-match-file-name
|
||||
class AudioData {
|
||||
AudioData({
|
||||
required this.url,
|
||||
required this.track,
|
||||
});
|
||||
|
||||
String url;
|
||||
Track track;
|
||||
}
|
||||
|
||||
@HostApi()
|
||||
abstract class MeditoAudioServiceApi {
|
||||
bool playAudio(AudioData audioData);
|
||||
|
||||
void playPauseAudio();
|
||||
|
||||
void stopAudio();
|
||||
|
||||
void setSpeed(double speed);
|
||||
|
||||
void seekToPosition(int position);
|
||||
|
||||
void skip10SecondsForward();
|
||||
|
||||
void skip10SecondsBackward();
|
||||
|
||||
void setBackgroundSound(String? uri);
|
||||
|
||||
void setBackgroundSoundVolume(double volume);
|
||||
|
||||
void stopBackgroundSound();
|
||||
|
||||
void playBackgroundSound();
|
||||
|
||||
}
|
||||
|
||||
// #enddocregion host-definitions
|
||||
|
||||
// #docregion flutter-definitions
|
||||
class PlaybackState {
|
||||
bool isPlaying;
|
||||
bool isBuffering;
|
||||
bool isSeeking;
|
||||
bool isCompleted;
|
||||
int position;
|
||||
int duration;
|
||||
Speed speed;
|
||||
int volume;
|
||||
Track track;
|
||||
BackgroundSound? backgroundSound;
|
||||
|
||||
PlaybackState({
|
||||
required this.isPlaying,
|
||||
required this.isBuffering,
|
||||
required this.isSeeking,
|
||||
required this.isCompleted,
|
||||
required this.position,
|
||||
required this.duration,
|
||||
required this.speed,
|
||||
required this.volume,
|
||||
required this.track,
|
||||
this.backgroundSound,
|
||||
});
|
||||
}
|
||||
|
||||
class BackgroundSound {
|
||||
String? uri;
|
||||
String title;
|
||||
|
||||
BackgroundSound({
|
||||
required this.uri,
|
||||
required this.title,
|
||||
});
|
||||
}
|
||||
|
||||
class Speed {
|
||||
double speed;
|
||||
|
||||
Speed({required this.speed});
|
||||
}
|
||||
|
||||
class Track {
|
||||
String title;
|
||||
String description;
|
||||
String imageUrl;
|
||||
String? artist;
|
||||
String? artistUrl;
|
||||
|
||||
Track({
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.imageUrl,
|
||||
required this.artist,
|
||||
this.artistUrl,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class MeditoAudioServiceCallbackApi {
|
||||
void updatePlaybackState(PlaybackState state);
|
||||
}
|
||||
// #enddocregion flutter-definitions
|
129
pubspec.lock
129
pubspec.lock
@ -73,38 +73,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
audio_service:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: audio_service
|
||||
sha256: a4d989f1225ea9621898d60f23236dcbfc04876fa316086c23c5c4af075dbac4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.12"
|
||||
audio_service_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audio_service_platform_interface
|
||||
sha256: "8431a455dac9916cc9ee6f7da5620a666436345c906ad2ebb7fa41d18b3c1bf4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
audio_service_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audio_service_web
|
||||
sha256: "523e64ddc914c714d53eec2da85bba1074f08cf26c786d4efb322de510815ea7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
audio_session:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: audio_session
|
||||
sha256: "6fdf255ed3af86535c96452c33ecff1245990bb25a605bfb1958661ccc3d467f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.18"
|
||||
auto_size_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -285,10 +253,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.1"
|
||||
version: "1.17.2"
|
||||
connectivity:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -776,31 +744,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.7.1"
|
||||
just_audio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: just_audio
|
||||
ref: "feature/treadmill"
|
||||
resolved-ref: "0b8047c2b5ef29a69f189b622a83b1d263fe1abb"
|
||||
url: "https://github.com/meditohq/just_audio.git"
|
||||
source: git
|
||||
version: "0.9.35"
|
||||
just_audio_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
sha256: c3dee0014248c97c91fe6299edb73dc4d6c6930a2f4f713579cd692d9e47f4a1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.2"
|
||||
just_audio_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_web
|
||||
sha256: "134356b0fe3d898293102b33b5fd618831ffdc72bb7a1b726140abdf22772b70"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.9"
|
||||
list_counter:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -837,18 +780,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.15"
|
||||
version: "0.12.16"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.5.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1041,6 +984,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.0"
|
||||
pigeon:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: pigeon
|
||||
sha256: "614d9926d887e8486b487de19d0ea082b7e91228f9e7e6ba86837daf5d6e71cb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
pin_code_fields:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1338,10 +1289,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1418,26 +1369,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4"
|
||||
sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.24.1"
|
||||
version: "1.24.3"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.6.0"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93"
|
||||
sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "0.5.3"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1466,10 +1417,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
|
||||
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.14"
|
||||
version: "6.2.1"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1514,10 +1465,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2
|
||||
sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.19"
|
||||
version: "2.2.0"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1566,6 +1517,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4-beta"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1586,18 +1545,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
|
||||
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.9"
|
||||
version: "5.1.1"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32_registry
|
||||
sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9
|
||||
sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.1.2"
|
||||
workmanager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: workmanager
|
||||
sha256: ed13530cccd28c5c9959ad42d657cd0666274ca74c56dea0ca183ddd527d3a00
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.2"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1623,5 +1590,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <3.7.0-13.0"
|
||||
flutter: ">=3.10.0"
|
||||
dart: ">=3.1.2 <3.7.0-13.0"
|
||||
flutter: ">=3.13.0"
|
||||
|
11
pubspec.yaml
11
pubspec.yaml
@ -11,7 +11,7 @@ description: A meditation learning tool
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 3.0.11+30011
|
||||
version: 3.1.0+30012
|
||||
environment:
|
||||
sdk: ">=2.18.0 <3.7.0-13.0"
|
||||
flutter: ">=3.0.0"
|
||||
@ -27,13 +27,6 @@ dependencies:
|
||||
flutter_markdown: ^0.6.7
|
||||
shared_preferences: ^2.0.5
|
||||
auto_size_text: ^3.0.0-nullsafety.0
|
||||
just_audio:
|
||||
git:
|
||||
url: https://github.com/meditohq/just_audio.git
|
||||
ref: feature/treadmill
|
||||
path: just_audio
|
||||
audio_service: ^0.18.10
|
||||
audio_session: ^0.1.18
|
||||
rxdart: ^0.27.1
|
||||
share: ^2.0.1
|
||||
flutter_html: ^3.0.0-alpha.2
|
||||
@ -67,6 +60,7 @@ dependencies:
|
||||
vibration: ^1.7.3
|
||||
reorderables: ^0.6.0
|
||||
google_api_availability: ^5.0.0
|
||||
workmanager: ^0.5.2
|
||||
|
||||
dependency_overrides:
|
||||
firebase_core_platform_interface: 4.5.1
|
||||
@ -74,6 +68,7 @@ dependency_overrides:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
pigeon: ^15.0.2
|
||||
mocktail: ^0.3.0
|
||||
build_runner: ^2.3.3
|
||||
riverpod_generator: ^2.1.0
|
||||
|
Loading…
Reference in New Issue
Block a user