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
|
flutter pub run dart_code_metrics:metrics check-unused-files --fatal-unused lib
|
||||||
|
|
||||||
# - name: Create or update PR comment
|
# - name: Create or update PR comment
|
||||||
# uses: peter-evans/create-or-update-comment@v1
|
# uses: peter-evans/create-or-update-comment@v1
|
||||||
# with:
|
# with:
|
||||||
# token: ${{ secrets.GITHUB_TOKEN }}
|
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
# repository: ${{ github.repository }}
|
# repository: ${{ github.repository }}
|
||||||
# issue-number: ${{ github.event.pull_request.number }}
|
# issue-number: ${{ github.event.pull_request.number }}
|
||||||
# body-file: metrics_report.txt
|
# body-file: metrics_report.txt
|
||||||
# edit-mode: replace
|
# edit-mode: replace
|
||||||
|
|
||||||
- name: Create a dummy .prod.env file
|
- name: Create a dummy .prod.env file
|
||||||
run: echo -e "var BASE_URL = ''; \n var SENTRY_URL = ''; \nvar INIT_TOKEN = '';" > .prod.env
|
run: echo -e "var BASE_URL = ''; \n var SENTRY_URL = ''; \nvar INIT_TOKEN = '';" > .prod.env
|
||||||
@ -85,6 +85,9 @@ jobs:
|
|||||||
- name: Run Riverpod generator
|
- name: Run Riverpod generator
|
||||||
run: flutter packages pub run build_runner build --delete-conflicting-outputs
|
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
|
- name: Analyze
|
||||||
run: flutter analyze --no-fatal-warnings --no-fatal-infos .
|
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/Runner.xcodeproj/project.pbxproj
|
||||||
ios/Flutter/AppFrameworkInfo.plist
|
ios/Flutter/AppFrameworkInfo.plist
|
||||||
android/app/src/google-services.json
|
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.
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: "5b12b7467fcbbdc7351d76690ce8a8693e804179"
|
revision: "d211f42860350d914a5ad8102f9ec32764dc6d06"
|
||||||
channel: "master"
|
channel: "stable"
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
||||||
@ -13,11 +13,26 @@ project_type: app
|
|||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: 5b12b7467fcbbdc7351d76690ce8a8693e804179
|
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||||
base_revision: 5b12b7467fcbbdc7351d76690ce8a8693e804179
|
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||||
|
- platform: android
|
||||||
|
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||||
|
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||||
- platform: ios
|
- platform: ios
|
||||||
create_revision: 5b12b7467fcbbdc7351d76690ce8a8693e804179
|
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
|
||||||
base_revision: 5b12b7467fcbbdc7351d76690ce8a8693e804179
|
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
|
# User provided section
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Generate Riverpod Files" type="ShConfigurationType">
|
<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="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||||
<option name="SCRIPT_PATH" value="" />
|
<option name="SCRIPT_PATH" value="" />
|
||||||
<option name="SCRIPT_OPTIONS" 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')
|
def appMinSdkVersion = localProperties.getProperty('flutter.minSdkVersion')
|
||||||
if (appMinSdkVersion == null) {
|
if (appMinSdkVersion == null) {
|
||||||
appMinSdkVersion = 21
|
appMinSdkVersion = 26
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
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"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 33
|
compileSdkVersion 34
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@ -110,6 +110,7 @@ android {
|
|||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
namespace 'meditofoundation.medito'
|
||||||
configurations.all {
|
configurations.all {
|
||||||
resolutionStrategy {
|
resolutionStrategy {
|
||||||
eachDependency {
|
eachDependency {
|
||||||
@ -130,7 +131,12 @@ dependencies {
|
|||||||
implementation platform('com.google.firebase:firebase-bom:32.1.1')
|
implementation platform('com.google.firebase:firebase-bom:32.1.1')
|
||||||
implementation 'com.google.firebase:firebase-analytics-ktx'
|
implementation 'com.google.firebase:firebase-analytics-ktx'
|
||||||
implementation "androidx.multidex:multidex:2.0.1"
|
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'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
implementation 'androidx.window:window:1.0.0'
|
implementation 'androidx.window:window:1.0.0'
|
||||||
implementation 'androidx.window:window-java: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"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="meditofoundation.medito">
|
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
the Flutter tool needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="meditofoundation.medito"
|
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
@ -34,23 +32,13 @@
|
|||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</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
|
<application
|
||||||
android:name="${applicationName}"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="Medito"
|
android:label="Medito"
|
||||||
android:usesCleartextTraffic="true">
|
android:usesCleartextTraffic="true">
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="ic_audiofileplayer"
|
|
||||||
android:value="drawable/logo" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.ryanheise.audioservice.AudioServiceActivity"
|
android:name=".MainActivity"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@ -60,89 +48,28 @@
|
|||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:windowSoftInputMode="adjustResize">
|
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>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
</activity>
|
</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
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||||
android:value="high_importance_channel" />
|
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>
|
</application>
|
||||||
</manifest>
|
</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
|
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.android.FlutterActivity
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.embedding.engine.FlutterEngineCache
|
||||||
import io.flutter.plugins.GeneratedPluginRegistrant
|
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||||
|
|
||||||
class MainActivity : FlutterActivity() {
|
class MainActivity : FlutterActivity() {
|
||||||
|
|
||||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||||
|
super.configureFlutterEngine(flutterEngine)
|
||||||
GeneratedPluginRegistrant.registerWith(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"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="meditofoundation.medito">
|
|
||||||
<!-- Flutter needs it to communicate with the running application
|
<!-- Flutter needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
|
@ -6,8 +6,8 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||||
classpath 'com.google.gms:google-services:4.3.15'
|
classpath 'com.google.gms:google-services:4.4.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
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 begin = 'Begin';
|
||||||
static const String none = 'None';
|
static const String none = 'None';
|
||||||
static const String removed = 'Removed';
|
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 supportEmail = 'hello@meditofoundation.org';
|
||||||
static const String pickNarratorAndDuration = 'Pick a narrator & duration';
|
static const String pickNarratorAndDuration = 'Pick a narrator & duration';
|
||||||
static const String pickDuration = 'Pick a duration';
|
static const String pickDuration = 'Pick a duration';
|
||||||
@ -48,8 +46,6 @@ class StringConstants {
|
|||||||
static const String x08 = 'X0.8';
|
static const String x08 = 'X0.8';
|
||||||
static const String x09 = 'X0.9';
|
static const String x09 = 'X0.9';
|
||||||
static const String x1 = 'X1';
|
static const String x1 = 'X1';
|
||||||
static const String x125 = 'X1.25';
|
|
||||||
static const String x15 = 'X1.5';
|
|
||||||
|
|
||||||
//Join
|
//Join
|
||||||
static const String joinTheMeditoFamily = 'Join the Medito Family';
|
static const String joinTheMeditoFamily = 'Join the Medito Family';
|
||||||
|
@ -12,20 +12,20 @@ const fontSize16 = 16.0;
|
|||||||
const fontSize14 = 14.0;
|
const fontSize14 = 14.0;
|
||||||
const smallScreenWidth = 480.0;
|
const smallScreenWidth = 480.0;
|
||||||
const miniPlayerHeight = 56.0;
|
const miniPlayerHeight = 56.0;
|
||||||
const height32 = SizedBox(height: 32);
|
const width2 = SizedBox(width: 2);
|
||||||
const height24 = SizedBox(height: 24);
|
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 height16 = SizedBox(height: 16);
|
||||||
const height20 = SizedBox(height: 20);
|
const height20 = SizedBox(height: 20);
|
||||||
const height12 = SizedBox(height: 12);
|
const height24 = SizedBox(height: 24);
|
||||||
const width16 = SizedBox(width: 16);
|
const height32 = SizedBox(height: 32);
|
||||||
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 bottomSheetBoxDecoration = BoxDecoration(
|
const bottomSheetBoxDecoration = BoxDecoration(
|
||||||
color: ColorConstants.onyx,
|
color: ColorConstants.onyx,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
|
@ -18,10 +18,9 @@ import 'package:Medito/constants/constants.dart';
|
|||||||
import 'package:Medito/constants/theme/app_theme.dart';
|
import 'package:Medito/constants/theme/app_theme.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
import 'package:Medito/providers/providers.dart';
|
||||||
import 'package:Medito/routes/routes.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/stats_utils.dart';
|
||||||
import 'package:Medito/utils/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:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.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:flutter_web_plugins/url_strategy.dart';
|
||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
|
import 'constants/environments/environment_constants.dart';
|
||||||
import 'services/notifications/notifications_service.dart';
|
import 'services/notifications/notifications_service.dart';
|
||||||
|
|
||||||
late AudioPlayerNotifier audioHandler;
|
var audioStateNotifier = AudioStateNotifier();
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await dotenv.load(fileName: StringConstants.stagingEnv);
|
|
||||||
|
await dotenv.load(fileName: EnvironmentConstants.stagingEnv);
|
||||||
|
|
||||||
|
MeditoAudioServiceCallbackApi.setup(AudioStateProvider(audioStateNotifier));
|
||||||
|
|
||||||
var sharedPreferences = await initializeSharedPreferences();
|
var sharedPreferences = await initializeSharedPreferences();
|
||||||
var isPlayServices = await checkGooglePlayServices();
|
var isPlayServices = await checkGooglePlayServices();
|
||||||
@ -56,8 +59,6 @@ Future<void> main() async {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
audioHandler = await initAudioService();
|
|
||||||
|
|
||||||
usePathUrlStrategy();
|
usePathUrlStrategy();
|
||||||
|
|
||||||
runApp(
|
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
|
// ignore: prefer-match-file-name
|
||||||
class ParentWidget extends ConsumerStatefulWidget {
|
class ParentWidget extends ConsumerStatefulWidget {
|
||||||
static const String _title = 'Medito';
|
static const String _title = 'Medito';
|
||||||
@ -116,12 +87,6 @@ class _ParentWidgetState extends ConsumerState<ParentWidget>
|
|||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
if (state == AppLifecycleState.resumed) {
|
if (state == AppLifecycleState.resumed) {
|
||||||
unawaited(updateStatsFromBg(ref));
|
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;
|
currentState = state;
|
||||||
}
|
}
|
||||||
@ -129,10 +94,6 @@ class _ParentWidgetState extends ConsumerState<ParentWidget>
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
|
||||||
audioProvider.trackAudioPlayer.dispose();
|
|
||||||
audioProvider.backgroundSoundAudioPlayer.dispose();
|
|
||||||
audioProvider.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,15 +116,9 @@ class _ParentWidgetState extends ConsumerState<ParentWidget>
|
|||||||
);
|
);
|
||||||
onMessageAppOpened(context, ref);
|
onMessageAppOpened(context, ref);
|
||||||
initializeNotification(context, ref);
|
initializeNotification(context, ref);
|
||||||
initializeAudioPlayer();
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializeAudioPlayer() {
|
|
||||||
var audioPlayerProvider = ref.read(audioPlayerNotifierProvider);
|
|
||||||
audioPlayerProvider.initAudioHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final goRouter = ref.watch(goRouterProvider);
|
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/save_fcm_token/save_fcm_token_model.dart';
|
||||||
export 'events/track_viewed/track_viewed_model.dart';
|
export 'events/track_viewed/track_viewed_model.dart';
|
||||||
export 'events/transfer_stats/transfer_stats_model.dart';
|
export 'events/transfer_stats/transfer_stats_model.dart';
|
||||||
|
export 'explore/explore_model.dart';
|
||||||
export 'home/announcement/announcement_model.dart';
|
export 'home/announcement/announcement_model.dart';
|
||||||
export 'home/chips/home_chips_items_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/header/home_header_model.dart';
|
||||||
export 'home/home_model.dart';
|
export 'home/home_model.dart';
|
||||||
export 'home/menu/home_menu_model.dart';
|
export 'home/menu/home_menu_model.dart';
|
||||||
export 'home/quote/quote_model.dart';
|
export 'home/quote/quote_model.dart';
|
||||||
export 'home/editorial/editorial_model.dart';
|
|
||||||
export 'home/rows/home_rows_model.dart';
|
export 'home/rows/home_rows_model.dart';
|
||||||
export 'home/shortcuts/shortcuts_model.dart';
|
export 'home/shortcuts/shortcuts_model.dart';
|
||||||
export 'join/join_route_params_model.dart';
|
export 'join/join_route_params_model.dart';
|
||||||
export 'me/me_model.dart';
|
export 'me/me_model.dart';
|
||||||
export 'notification/notification_payload_model.dart';
|
export 'notification/notification_payload_model.dart';
|
||||||
export 'pack/pack_model.dart';
|
export 'pack/pack_model.dart';
|
||||||
export 'explore/explore_model.dart';
|
|
||||||
export 'stats/all_stats/all_stats_model.dart';
|
export 'stats/all_stats/all_stats_model.dart';
|
||||||
export 'stats/stats_model.dart';
|
export 'stats/stats_model.dart';
|
||||||
export 'stats/tiles/tiles_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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.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
|
@riverpod
|
||||||
Future<List<BackgroundSoundsModel>> backgroundSounds(BackgroundSoundsRef ref) {
|
Future<List<BackgroundSoundsModel>> backgroundSounds(BackgroundSoundsRef ref) {
|
||||||
@ -43,7 +47,7 @@ final backgroundSoundsNotifierProvider =
|
|||||||
//ignore:prefer-match-file-name
|
//ignore:prefer-match-file-name
|
||||||
class BackgroundSoundsNotifier extends ChangeNotifier {
|
class BackgroundSoundsNotifier extends ChangeNotifier {
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
double volume = 0;
|
double volume = 50;
|
||||||
BackgroundSoundsModel? selectedBgSound;
|
BackgroundSoundsModel? selectedBgSound;
|
||||||
|
|
||||||
BackgroundSoundsNotifier(this.ref);
|
BackgroundSoundsNotifier(this.ref);
|
||||||
@ -51,18 +55,21 @@ class BackgroundSoundsNotifier extends ChangeNotifier {
|
|||||||
void handleOnChangeVolume(double vol) {
|
void handleOnChangeVolume(double vol) {
|
||||||
volume = vol;
|
volume = vol;
|
||||||
ref.read(backgroundSoundsRepositoryProvider).handleOnChangeVolume(vol);
|
ref.read(backgroundSoundsRepositoryProvider).handleOnChangeVolume(vol);
|
||||||
|
_api.setBackgroundSoundVolume(volume);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleOnChangeSound(BackgroundSoundsModel? sound) {
|
void handleOnChangeSound(BackgroundSoundsModel? sound) {
|
||||||
selectedBgSound = sound;
|
selectedBgSound = sound;
|
||||||
var bgSoundRepoProvider = ref.read(backgroundSoundsRepositoryProvider);
|
var bgSoundRepoProvider = ref.read(backgroundSoundsRepositoryProvider);
|
||||||
|
|
||||||
if (sound != null) {
|
if (sound != null) {
|
||||||
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
|
||||||
bgSoundRepoProvider.handleOnChangeSound(sound);
|
bgSoundRepoProvider.handleOnChangeSound(sound);
|
||||||
updateItemsInSavedBgSoundList(sound);
|
_updateItemsInSavedBgSoundList(sound);
|
||||||
|
|
||||||
if (sound.title != StringConstants.none) {
|
if (sound.title != StringConstants.none) {
|
||||||
var name = '${sound.title}.mp3';
|
var name = '${sound.title}.mp3';
|
||||||
|
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
||||||
downloadAudio.getDownloadedFile(name).then((value) {
|
downloadAudio.getDownloadedFile(name).then((value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
downloadAudio.downloadFile(
|
downloadAudio.downloadFile(
|
||||||
@ -70,15 +77,25 @@ class BackgroundSoundsNotifier extends ChangeNotifier {
|
|||||||
name: name,
|
name: name,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
_api.setBackgroundSound(value);
|
||||||
|
_api.playBackgroundSound();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bgSoundRepoProvider.removeSelectedBgSound();
|
stopBackgroundSound();
|
||||||
}
|
}
|
||||||
notifyListeners();
|
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);
|
final provider = ref.read(backgroundSoundsRepositoryProvider);
|
||||||
provider.updateItemsInSavedBgSoundList(sound);
|
provider.updateItemsInSavedBgSoundList(sound);
|
||||||
}
|
}
|
||||||
@ -94,12 +111,9 @@ class BackgroundSoundsNotifier extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void getVolumeFromPref() {
|
void getVolumeFromPref() {
|
||||||
var value = ref.read(backgroundSoundsRepositoryProvider).getBgSoundVolume();
|
var value =
|
||||||
if (value != null) {
|
ref.read(backgroundSoundsRepositoryProvider).getBgSoundVolume() ?? 50.0;
|
||||||
volume = value;
|
handleOnChangeVolume(value);
|
||||||
notifyListeners();
|
volume = value;
|
||||||
} else {
|
|
||||||
handleOnChangeVolume(50.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
import 'package:Medito/constants/constants.dart';
|
||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:Medito/repositories/repositories.dart';
|
import 'package:Medito/repositories/repositories.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
import '../me/me_provider.dart';
|
||||||
|
|
||||||
part 'device_and_app_info_provider.g.dart';
|
part 'device_and_app_info_provider.g.dart';
|
||||||
|
|
||||||
@riverpod
|
@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)}';
|
'${trackModel.id}-${file.id}${getAudioFileExtension(file.path)}';
|
||||||
try {
|
try {
|
||||||
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
||||||
audioDownloadState[fileName] = AUDIO_DOWNLOAD_STATE.DOWNLOADIING;
|
audioDownloadState[fileName] = AUDIO_DOWNLOAD_STATE.DOWNLOADING;
|
||||||
await downloadAudio.downloadFile(
|
await downloadAudio.downloadFile(
|
||||||
file.path,
|
file.path,
|
||||||
name: fileName,
|
name: fileName,
|
||||||
@ -67,7 +67,7 @@ class AudioDownloaderProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getTrackAudio(String fileName) async {
|
Future<String?> getTrackPath(String fileName) async {
|
||||||
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
final downloadAudio = ref.read(downloaderRepositoryProvider);
|
||||||
var audioPath = await downloadAudio.getDownloadedFile(fileName);
|
var audioPath = await downloadAudio.getDownloadedFile(fileName);
|
||||||
audioDownloadState[fileName] = audioPath != null
|
audioDownloadState[fileName] = audioPath != null
|
||||||
@ -81,6 +81,6 @@ class AudioDownloaderProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
enum AUDIO_DOWNLOAD_STATE {
|
enum AUDIO_DOWNLOAD_STATE {
|
||||||
DOWNLOAD,
|
DOWNLOAD,
|
||||||
DOWNLOADIING,
|
DOWNLOADING,
|
||||||
DOWNLOADED,
|
DOWNLOADED,
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:Medito/constants/constants.dart';
|
|
||||||
import 'package:Medito/models/models.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: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 =
|
final playerProvider =
|
||||||
StateNotifierProvider<PlayerProvider, TrackModel?>((ref) {
|
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(
|
await _playTrack(
|
||||||
ref,
|
ref,
|
||||||
audioPlayerNotifier,
|
|
||||||
track,
|
track,
|
||||||
file,
|
file,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
void _optionallyLoadBackgroundSound(
|
state = track;
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _playTrack(
|
Future<void> _playTrack(
|
||||||
Ref ref,
|
Ref ref,
|
||||||
AudioPlayerNotifier audioPlayerNotifier,
|
|
||||||
TrackModel trackModel,
|
TrackModel trackModel,
|
||||||
TrackFilesModel file,
|
TrackFilesModel file,
|
||||||
) async {
|
) 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),
|
_constructFileName(trackModel, file),
|
||||||
);
|
);
|
||||||
var filePath = await checkDownloadedFile;
|
await _api.playAudio(
|
||||||
audioPlayerNotifier.setTrackAudio(
|
AudioData(
|
||||||
trackModel,
|
url: downloadPath ?? file.path,
|
||||||
file,
|
track: Track(
|
||||||
filePath: filePath,
|
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) =>
|
String _constructFileName(TrackModel trackModel, TrackFilesModel file) =>
|
||||||
'${trackModel.id}-${file.id}${getAudioFileExtension(file.path)}';
|
'${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(
|
void handleAudioStartedEvent(
|
||||||
String trackId,
|
String trackId,
|
||||||
String audioFileId,
|
String audioFileId,
|
||||||
@ -123,19 +124,28 @@ class PlayerProvider extends StateNotifier<TrackModel?> {
|
|||||||
ref.read(eventsProvider(event: event.toJson()));
|
ref.read(eventsProvider(event: event.toJson()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAudioCompletionEvent(
|
Future<void> seekToPosition(int position) async {
|
||||||
String audioFileId,
|
await _api.seekToPosition(position);
|
||||||
String trackId,
|
}
|
||||||
) {
|
|
||||||
var audio = AudioCompletedModel(
|
void stop() {
|
||||||
audioFileId: audioFileId,
|
_cancelBackgroundThreadForAudioCompleteEvent();
|
||||||
trackId: trackId,
|
_api.stopAudio();
|
||||||
updateStats: true,
|
}
|
||||||
);
|
|
||||||
var event = EventsModel(
|
void setSpeed(double speed) {
|
||||||
name: EventTypes.audioCompleted,
|
_api.setSpeed(speed);
|
||||||
payload: audio.toJson(),
|
}
|
||||||
);
|
|
||||||
ref.read(eventsProvider(event: event.toJson()));
|
void skip10SecondsForward() {
|
||||||
|
_api.skip10SecondsForward();
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip10SecondsBackward() {
|
||||||
|
_api.skip10SecondsBackward();
|
||||||
|
}
|
||||||
|
|
||||||
|
void playPause() {
|
||||||
|
_api.playPauseAudio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,16 @@
|
|||||||
export 'auth/auth_provider.dart';
|
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 'connectivity/connectivity_provider.dart';
|
||||||
export 'device_and_app_info/device_and_app_info_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 'events/events_provider.dart';
|
||||||
|
export 'explore/explore_provider.dart';
|
||||||
export 'meditation/download_track_provider.dart';
|
export 'meditation/download_track_provider.dart';
|
||||||
export 'meditation/track_provider.dart';
|
export 'meditation/track_provider.dart';
|
||||||
export 'notification/notification_provider.dart';
|
export 'notification/notification_provider.dart';
|
||||||
export 'pack/pack_provider.dart';
|
export 'pack/pack_provider.dart';
|
||||||
export 'page_view/bottom_padding_provider.dart';
|
export 'page_view/bottom_padding_provider.dart';
|
||||||
export 'player/audio_play_pause_provider.dart';
|
export 'player/audio_state_provider.dart';
|
||||||
export 'player/audio_position_provider.dart';
|
|
||||||
export 'player/audio_speed_provider.dart';
|
|
||||||
export 'player/download/audio_downloader_provider.dart';
|
export 'player/download/audio_downloader_provider.dart';
|
||||||
export 'player/player_provider.dart';
|
export 'player/player_provider.dart';
|
||||||
export 'explore/explore_provider.dart';
|
|
||||||
export 'shared_preference/shared_preference_provider.dart';
|
export 'shared_preference/shared_preference_provider.dart';
|
||||||
export 'events/app_opened_event_provider.dart';
|
|
||||||
export 'stats/stats_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/providers/providers.dart';
|
||||||
import 'package:Medito/routes/routes.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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) {
|
final rootCombineProvider = Provider.family<void, BuildContext>((ref, context) {
|
||||||
ref.read(remoteStatsProvider);
|
ref.read(remoteStatsProvider);
|
||||||
@ -13,54 +8,4 @@ final rootCombineProvider = Provider.family<void, BuildContext>((ref, context) {
|
|||||||
ref.read(postLocalStatsProvider);
|
ref.read(postLocalStatsProvider);
|
||||||
ref.read(deviceAppAndUserInfoProvider);
|
ref.read(deviceAppAndUserInfoProvider);
|
||||||
ref.read(audioDownloaderProvider).deleteDownloadedFileFromPreviousVersion();
|
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/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
import 'package:Medito/providers/providers.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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:dio/dio.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@ -78,5 +77,5 @@ class AuthRepositoryImpl extends AuthRepository {
|
|||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
AuthRepository authRepository(AuthRepositoryRef ref) {
|
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/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
import 'package:Medito/providers/providers.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
@ -159,7 +158,7 @@ BackgroundSoundsRepositoryImpl backgroundSoundsRepository(
|
|||||||
BackgroundSoundsRepositoryRef ref,
|
BackgroundSoundsRepositoryRef ref,
|
||||||
) {
|
) {
|
||||||
return BackgroundSoundsRepositoryImpl(
|
return BackgroundSoundsRepositoryImpl(
|
||||||
client: ref.watch(dioClientProvider),
|
client: DioApiService(),
|
||||||
ref: ref,
|
ref: ref,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import 'dart:io';
|
|||||||
import 'package:Medito/constants/strings/shared_preference_constants.dart';
|
import 'package:Medito/constants/strings/shared_preference_constants.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
import 'package:Medito/providers/providers.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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:Medito/utils/utils.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -115,7 +114,7 @@ class DownloaderRepositoryImpl extends DownloaderRepository {
|
|||||||
@riverpod
|
@riverpod
|
||||||
DownloaderRepositoryImpl downloaderRepository(DownloaderRepositoryRef ref) {
|
DownloaderRepositoryImpl downloaderRepository(DownloaderRepositoryRef ref) {
|
||||||
return DownloaderRepositoryImpl(
|
return DownloaderRepositoryImpl(
|
||||||
client: ref.watch(dioClientProvider),
|
client: DioApiService(),
|
||||||
ref: ref,
|
ref: ref,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
import 'package:Medito/constants/constants.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
part 'events_repository.g.dart';
|
part 'events_repository.g.dart';
|
||||||
@ -35,5 +34,5 @@ class EventsRepositoryImpl extends EventsRepository {
|
|||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
EventsRepository eventsRepository(EventsRepositoryRef ref) {
|
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/constants/constants.dart';
|
||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
part 'explore_repository.g.dart';
|
part 'explore_repository.g.dart';
|
||||||
@ -36,6 +35,6 @@ class ExploreRepositoryImpl extends ExploreRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
ExploreRepositoryImpl exploreRepository(ref) {
|
ExploreRepositoryImpl exploreRepository(ExploreRepositoryRef ref) {
|
||||||
return ExploreRepositoryImpl(client: ref.watch(dioClientProvider));
|
return ExploreRepositoryImpl(client: DioApiService());
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import 'package:Medito/constants/constants.dart';
|
|||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
import 'package:Medito/providers/providers.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
@ -114,5 +113,5 @@ class HomeRepositoryImpl extends HomeRepository {
|
|||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
HomeRepositoryImpl homeRepository(HomeRepositoryRef ref) {
|
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/constants/constants.dart';
|
||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
part 'me_repository.g.dart';
|
part 'me_repository.g.dart';
|
||||||
@ -25,5 +24,5 @@ class MeRepositoryImpl extends MeRepository {
|
|||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
MeRepository meRepository(MeRepositoryRef ref) {
|
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/constants/constants.dart';
|
||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
part 'packs_repository.g.dart';
|
part 'packs_repository.g.dart';
|
||||||
@ -35,5 +34,5 @@ class PackRepositoryImpl extends PacksRepository {
|
|||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
PackRepositoryImpl packRepository(PackRepositoryRef ref) {
|
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 'device_and_app_info/device_and_app_info_repository.dart';
|
||||||
export 'downloader/downloader_repository.dart';
|
export 'downloader/downloader_repository.dart';
|
||||||
export 'events/events_repository.dart';
|
export 'events/events_repository.dart';
|
||||||
|
export 'explore/explore_repository.dart';
|
||||||
export 'home/home_repository.dart';
|
export 'home/home_repository.dart';
|
||||||
export 'me/me_repository.dart';
|
export 'me/me_repository.dart';
|
||||||
export 'pack/packs_repository.dart';
|
export 'pack/packs_repository.dart';
|
||||||
export 'explore/explore_repository.dart';
|
|
||||||
export 'stats/stats_repository.dart';
|
export 'stats/stats_repository.dart';
|
||||||
export 'track/track_repository.dart';
|
export 'track/track_repository.dart';
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
import 'package:Medito/constants/constants.dart';
|
||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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:Medito/utils/stats_utils.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
@ -98,6 +97,6 @@ class StatsRepositoryImpl extends StatsRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
StatsRepository statsRepository(StatsRepositoryRef ref) {
|
StatsRepository statsRepository(StatsRepositoryRef _) {
|
||||||
return StatsRepositoryImpl(client: ref.watch(dioClientProvider));
|
return StatsRepositoryImpl(client: DioApiService());
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import 'package:Medito/constants/constants.dart';
|
|||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
import 'package:Medito/providers/providers.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
@ -97,5 +96,5 @@ class TrackRepositoryImpl extends TrackRepository {
|
|||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
TrackRepository trackRepository(TrackRepositoryRef ref) {
|
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/bottom_navigation/bottom_navigation_bar_view.dart';
|
||||||
import 'package:Medito/views/downloads/downloads_view.dart';
|
import 'package:Medito/views/downloads/downloads_view.dart';
|
||||||
import 'package:Medito/views/end_screen/end_screen_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/home/home_view.dart';
|
||||||
import 'package:Medito/views/missing_route/missing_route_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/notifications/notification_permission_view.dart';
|
||||||
import 'package:Medito/views/pack/pack_view.dart';
|
import 'package:Medito/views/pack/pack_view.dart';
|
||||||
import 'package:Medito/views/player/player_view.dart';
|
import 'package:Medito/views/player/player_view.dart';
|
||||||
import 'package:Medito/views/root/root_page_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/splash_view.dart';
|
||||||
import 'package:Medito/views/track/track_view.dart';
|
import 'package:Medito/views/track/track_view.dart';
|
||||||
import 'package:Medito/widgets/widgets.dart';
|
import 'package:Medito/widgets/widgets.dart';
|
||||||
|
@ -1,10 +1,72 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
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
|
// ignore: avoid_dynamic_calls
|
||||||
class DioApiService {
|
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
|
// ignore: avoid-dynamic
|
||||||
Future<dynamic> getRequest(
|
Future<dynamic> getRequest(
|
||||||
|
@ -1,74 +1,14 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:Medito/constants/constants.dart';
|
|
||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
import 'package:Medito/providers/providers.dart';
|
||||||
import 'package:Medito/services/network/dio_api_service.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: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 assignDioHeadersProvider = FutureProvider<void>((ref) async {
|
||||||
var auth = ref.read(authProvider);
|
var auth = ref.read(authProvider);
|
||||||
var deviceInfo = await ref.read(deviceAndAppInfoProvider.future);
|
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;
|
var user = auth.userResponse.body as UserTokenModel;
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ Future<bool> updateMinuteCounter(int additionalSecs) async {
|
|||||||
var prefs = await SharedPreferences.getInstance();
|
var prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
var current = await _getSecondsListened();
|
var current = await _getSecondsListened();
|
||||||
var plusOne = current + (additionalSecs);
|
var plusOne = current + additionalSecs;
|
||||||
await prefs.setInt(SharedPreferenceConstants.secsListened, plusOne);
|
await prefs.setInt(SharedPreferenceConstants.secsListened, plusOne);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -110,8 +110,6 @@ Future<void> launchEmailSubmission(
|
|||||||
|
|
||||||
if (await canLaunchUrl(params)) {
|
if (await canLaunchUrl(params)) {
|
||||||
await launchUrl(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:go_router/go_router.dart';
|
||||||
import 'package:pin_code_fields/pin_code_fields.dart';
|
import 'package:pin_code_fields/pin_code_fields.dart';
|
||||||
|
|
||||||
|
import '../../providers/me/me_provider.dart';
|
||||||
import '../../utils/utils.dart';
|
import '../../utils/utils.dart';
|
||||||
|
|
||||||
class JoinVerifyOTPView extends ConsumerStatefulWidget {
|
class JoinVerifyOTPView extends ConsumerStatefulWidget {
|
||||||
|
@ -5,6 +5,7 @@ import 'package:Medito/widgets/widgets.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../providers/background_sounds/background_sounds_notifier.dart';
|
||||||
import 'widgets/sound_listtile_widget.dart';
|
import 'widgets/sound_listtile_widget.dart';
|
||||||
import 'widgets/volume_slider_widget.dart';
|
import 'widgets/volume_slider_widget.dart';
|
||||||
|
|
||||||
@ -23,36 +24,6 @@ class _BackgroundSoundViewState extends ConsumerState<BackgroundSoundView> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_scrollController.addListener(_scrollListener);
|
_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() {
|
void _scrollListener() {
|
||||||
@ -151,8 +122,7 @@ class _BackgroundSoundViewState extends ConsumerState<BackgroundSoundView> {
|
|||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
height16,
|
height32,
|
||||||
height16,
|
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
import 'package:Medito/constants/constants.dart';
|
||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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 {
|
class SoundListTileWidget extends ConsumerWidget {
|
||||||
const SoundListTileWidget({required this.sound}) : super();
|
const SoundListTileWidget({required this.sound}) : super();
|
||||||
@ -12,7 +12,6 @@ class SoundListTileWidget extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final bgSoundNotifierProvider = ref.watch(backgroundSoundsNotifierProvider);
|
final bgSoundNotifierProvider = ref.watch(backgroundSoundsNotifierProvider);
|
||||||
final audioPlayerNotifier = ref.watch(audioPlayerNotifierProvider);
|
|
||||||
var selectedSoundId = bgSoundNotifierProvider.selectedBgSound?.id;
|
var selectedSoundId = bgSoundNotifierProvider.selectedBgSound?.id;
|
||||||
var id = selectedSoundId ?? '0';
|
var id = selectedSoundId ?? '0';
|
||||||
var isSelected = id == sound.id;
|
var isSelected = id == sound.id;
|
||||||
@ -20,7 +19,6 @@ class SoundListTileWidget extends ConsumerWidget {
|
|||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => _handleItemTap(
|
onTap: () => _handleItemTap(
|
||||||
bgSoundNotifierProvider,
|
bgSoundNotifierProvider,
|
||||||
audioPlayerNotifier,
|
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -66,17 +64,10 @@ class SoundListTileWidget extends ConsumerWidget {
|
|||||||
|
|
||||||
void _handleItemTap(
|
void _handleItemTap(
|
||||||
BackgroundSoundsNotifier bgSoundNotifierProvider,
|
BackgroundSoundsNotifier bgSoundNotifierProvider,
|
||||||
AudioPlayerNotifier audioPlayerNotifier,
|
|
||||||
) {
|
) {
|
||||||
if (sound.title == StringConstants.none) {
|
if (sound.title == StringConstants.none) {
|
||||||
bgSoundNotifierProvider.handleOnChangeSound(sound);
|
bgSoundNotifierProvider.stopBackgroundSound();
|
||||||
audioPlayerNotifier.stopBackgroundSound();
|
|
||||||
audioPlayerNotifier.backgroundSoundAudioPlayer.dispose();
|
|
||||||
audioPlayerNotifier.backgroundSoundAudioPlayer = AudioPlayer();
|
|
||||||
} else {
|
|
||||||
audioPlayerNotifier.setBackgroundAudio(sound);
|
|
||||||
audioPlayerNotifier.playBackgroundSound();
|
|
||||||
bgSoundNotifierProvider.handleOnChangeSound(sound);
|
|
||||||
}
|
}
|
||||||
|
bgSoundNotifierProvider.handleOnChangeSound(sound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
import 'package:Medito/constants/constants.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../../providers/background_sounds/background_sounds_notifier.dart';
|
||||||
import 'background_sound_volume_track_shape_widget.dart';
|
import 'background_sound_volume_track_shape_widget.dart';
|
||||||
|
|
||||||
class VolumeSliderWidget extends ConsumerWidget {
|
class VolumeSliderWidget extends ConsumerWidget {
|
||||||
@ -11,14 +11,13 @@ class VolumeSliderWidget extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final bgSoundNotifierProvider = ref.watch(backgroundSoundsNotifierProvider);
|
final bgSoundNotifierProvider = ref.watch(backgroundSoundsNotifierProvider);
|
||||||
final audioPlayerNotifier = ref.watch(audioPlayerNotifierProvider);
|
final currentVolume = bgSoundNotifierProvider.volume;
|
||||||
var currentVolume = bgSoundNotifierProvider.volume;
|
|
||||||
|
|
||||||
return SliderTheme(
|
return SliderTheme(
|
||||||
data: SliderThemeData(
|
data: SliderThemeData(
|
||||||
trackShape: BackgroundSoundVolumeTrackShapeWidget(
|
trackShape: BackgroundSoundVolumeTrackShapeWidget(
|
||||||
leadingTitle: StringConstants.volume,
|
leadingTitle: StringConstants.volume,
|
||||||
tralingText: currentVolume.toString().split('.').first + '%',
|
tralingText: currentVolume.toString().split('.').first + '%',
|
||||||
),
|
),
|
||||||
overlayShape: RoundSliderOverlayShape(overlayRadius: 0.0),
|
overlayShape: RoundSliderOverlayShape(overlayRadius: 0.0),
|
||||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 0.0),
|
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 0.0),
|
||||||
@ -33,7 +32,6 @@ class VolumeSliderWidget extends ConsumerWidget {
|
|||||||
inactiveColor: ColorConstants.greyIsTheNewGrey,
|
inactiveColor: ColorConstants.greyIsTheNewGrey,
|
||||||
onChanged: (double newValue) {
|
onChanged: (double newValue) {
|
||||||
bgSoundNotifierProvider.handleOnChangeVolume(newValue);
|
bgSoundNotifierProvider.handleOnChangeVolume(newValue);
|
||||||
audioPlayerNotifier.setBackgroundSoundVolume(newValue);
|
|
||||||
},
|
},
|
||||||
semanticFormatterCallback: (double newValue) {
|
semanticFormatterCallback: (double newValue) {
|
||||||
return '${newValue.round()} ';
|
return '${newValue.round()} ';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
import 'package:Medito/constants/constants.dart';
|
||||||
import 'package:Medito/providers/providers.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/explore/explore_view.dart';
|
||||||
|
import 'package:Medito/views/home/home_view.dart';
|
||||||
import 'package:Medito/widgets/widgets.dart';
|
import 'package:Medito/widgets/widgets.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.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 {
|
class DownloadsView extends ConsumerStatefulWidget {
|
||||||
@override
|
@override
|
||||||
ConsumerState<DownloadsView> createState() => _DownloadsViewState();
|
ConsumerState<DownloadsView> createState() => _DownloadsViewState();
|
||||||
@ -148,8 +151,8 @@ class _DownloadsViewState extends ConsumerState<DownloadsView>
|
|||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
TrackModel trackModel,
|
TrackModel trackModel,
|
||||||
) async {
|
) async {
|
||||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
final bgSoundNotifier = ref.read(backgroundSoundsNotifierProvider);
|
||||||
await audioProvider.stop();
|
bgSoundNotifier.getVolumeFromPref();
|
||||||
await ref.read(playerProvider.notifier).loadSelectedTrack(
|
await ref.read(playerProvider.notifier).loadSelectedTrack(
|
||||||
trackModel: trackModel,
|
trackModel: trackModel,
|
||||||
file: trackModel.audio.first.files.first,
|
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/models/models.dart';
|
||||||
import 'package:Medito/widgets/headers/medito_app_bar_small.dart';
|
import 'package:Medito/widgets/headers/medito_app_bar_small.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'widgets/donation_widget.dart';
|
import 'widgets/donation_widget.dart';
|
||||||
import 'widgets/feedback_widget.dart';
|
import 'widgets/feedback_widget.dart';
|
||||||
|
|
||||||
class EndScreenView extends StatelessWidget {
|
class EndScreenView extends ConsumerStatefulWidget {
|
||||||
final List<EndScreenModel> endScreenModel;
|
final List<EndScreenModel> endScreenModel;
|
||||||
|
|
||||||
const EndScreenView({
|
const EndScreenView({
|
||||||
@ -14,6 +15,12 @@ class EndScreenView extends StatelessWidget {
|
|||||||
required this.endScreenModel,
|
required this.endScreenModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<EndScreenView> createState() => _EndScreenViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EndScreenViewState extends ConsumerState<EndScreenView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -35,7 +42,7 @@ class EndScreenView extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: padding16),
|
padding: const EdgeInsets.symmetric(horizontal: padding16),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: endScreenModel.map((e) {
|
children: widget.endScreenModel.map((e) {
|
||||||
if (e.name == TypeConstants.donationAskCard) {
|
if (e.name == TypeConstants.donationAskCard) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 20),
|
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/constants/constants.dart';
|
||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
@ -18,33 +17,33 @@ class _FeedbackWidgetState extends ConsumerState<FeedbackWidget> {
|
|||||||
bool isFeedbackAdded = false;
|
bool isFeedbackAdded = false;
|
||||||
|
|
||||||
void _handleFeedbackPress(String feedback) async {
|
void _handleFeedbackPress(String feedback) async {
|
||||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
// final audioProvider = ref.read(audioPlayerNotifierProvider);
|
||||||
var extras = audioProvider.mediaItem.value?.extras;
|
// var extras = audioProvider.mediaItem.value?.extras;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
});
|
});
|
||||||
var payload = FeedbackTappedModel(
|
// var payload = FeedbackTappedModel(
|
||||||
trackId: extras?[TypeConstants.trackIdKey] ?? '',
|
// trackId: extras?[TypeConstants.trackIdKey] ?? '',
|
||||||
audioFileId: extras?[TypeConstants.fileIdKey] ?? '',
|
// audioFileId: extras?[TypeConstants.fileIdKey] ?? '',
|
||||||
emoji: feedback,
|
// emoji: feedback,
|
||||||
);
|
// );
|
||||||
var event = EventsModel(
|
// var event = EventsModel(
|
||||||
name: EventTypes.trackFeedback,
|
// name: EventTypes.trackFeedback,
|
||||||
payload: payload.toJson(),
|
// payload: payload.toJson(),
|
||||||
);
|
// );
|
||||||
try {
|
// try {
|
||||||
await ref.read(eventsProvider(event: event.toJson()).future);
|
// await ref.read(eventsProvider(event: event.toJson()).future);
|
||||||
setState(() {
|
// setState(() {
|
||||||
isLoading = false;
|
// isLoading = false;
|
||||||
isFeedbackAdded = true;
|
// isFeedbackAdded = true;
|
||||||
});
|
// });
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
setState(() {
|
// setState(() {
|
||||||
isLoading = false;
|
// isLoading = false;
|
||||||
isFeedbackAdded = false;
|
// isFeedbackAdded = false;
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -6,6 +6,8 @@ import 'package:Medito/views/home/widgets/header_and_announcement_widget.dart';
|
|||||||
import 'package:Medito/widgets/widgets.dart';
|
import 'package:Medito/widgets/widgets.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../providers/home/home_provider.dart';
|
||||||
import 'widgets/editorial/editorial_widget.dart';
|
import 'widgets/editorial/editorial_widget.dart';
|
||||||
import 'widgets/quote/quote_widget.dart';
|
import 'widgets/quote/quote_widget.dart';
|
||||||
import 'widgets/shortcuts/shortcuts_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/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../../../../providers/me/me_provider.dart';
|
||||||
import '../share_btn/share_btn_widget.dart';
|
import '../share_btn/share_btn_widget.dart';
|
||||||
|
|
||||||
class DebugBottomSheetWidget extends ConsumerWidget {
|
class DebugBottomSheetWidget extends ConsumerWidget {
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import 'package:Medito/constants/colors/color_constants.dart';
|
import 'package:Medito/constants/colors/color_constants.dart';
|
||||||
import 'package:Medito/constants/styles/widget_styles.dart';
|
import 'package:Medito/constants/styles/widget_styles.dart';
|
||||||
import 'package:Medito/models/home/editorial/editorial_model.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/routes/routes.dart';
|
||||||
import 'package:Medito/utils/utils.dart';
|
import 'package:Medito/utils/utils.dart';
|
||||||
import 'package:Medito/widgets/widgets.dart';
|
import 'package:Medito/widgets/widgets.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../../../providers/home/home_provider.dart';
|
||||||
import '../animated_scale_widget.dart';
|
import '../animated_scale_widget.dart';
|
||||||
|
|
||||||
class EditorialWidget extends ConsumerWidget {
|
class EditorialWidget extends ConsumerWidget {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import 'package:Medito/models/models.dart';
|
import 'package:Medito/models/models.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:Medito/widgets/widgets.dart';
|
import 'package:Medito/widgets/widgets.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../../providers/home/home_provider.dart';
|
||||||
import 'announcement/announcement_widget.dart';
|
import 'announcement/announcement_widget.dart';
|
||||||
import 'header/home_header_widget.dart';
|
import 'header/home_header_widget.dart';
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import 'package:Medito/constants/colors/color_constants.dart';
|
import 'package:Medito/constants/colors/color_constants.dart';
|
||||||
import 'package:Medito/constants/styles/widget_styles.dart';
|
import 'package:Medito/constants/styles/widget_styles.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:Medito/widgets/widgets.dart';
|
import 'package:Medito/widgets/widgets.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../../../providers/home/home_provider.dart';
|
||||||
|
|
||||||
class QuoteWidget extends ConsumerWidget {
|
class QuoteWidget extends ConsumerWidget {
|
||||||
const QuoteWidget({super.key});
|
const QuoteWidget({super.key});
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:reorderables/reorderables.dart';
|
import 'package:reorderables/reorderables.dart';
|
||||||
|
|
||||||
|
import '../../../../providers/home/home_provider.dart';
|
||||||
import '../animated_scale_widget.dart';
|
import '../animated_scale_widget.dart';
|
||||||
|
|
||||||
class ShortcutsItemsWidget extends ConsumerStatefulWidget {
|
class ShortcutsItemsWidget extends ConsumerStatefulWidget {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:Medito/widgets/widgets.dart';
|
import 'package:Medito/widgets/widgets.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../../../providers/home/home_provider.dart';
|
||||||
import 'shortcuts_items_widget.dart';
|
import 'shortcuts_items_widget.dart';
|
||||||
|
|
||||||
class ShortcutsWidget extends ConsumerStatefulWidget {
|
class ShortcutsWidget extends ConsumerStatefulWidget {
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
|
||||||
import 'package:Medito/providers/providers.dart';
|
import 'package:Medito/providers/providers.dart';
|
||||||
import 'package:Medito/widgets/headers/medito_app_bar_small.dart';
|
import 'package:Medito/views/player/widgets/artist_title_widget.dart';
|
||||||
import 'package:Medito/widgets/widgets.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/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import 'widgets/artist_title_widget.dart';
|
import '../../constants/strings/route_constants.dart';
|
||||||
import 'widgets/bottom_actions/bottom_action_widget.dart';
|
import '../../constants/strings/string_constants.dart';
|
||||||
import 'widgets/duration_indicator_widget.dart';
|
import '../../providers/background_sounds/background_sounds_notifier.dart';
|
||||||
import 'widgets/overlay_cover_image_widget.dart';
|
import '../../widgets/errors/medito_error_widget.dart';
|
||||||
import 'widgets/player_buttons/player_buttons_widget.dart';
|
import '../../widgets/headers/medito_app_bar_small.dart';
|
||||||
|
|
||||||
class PlayerView extends ConsumerStatefulWidget {
|
class PlayerView extends ConsumerStatefulWidget {
|
||||||
const PlayerView({
|
const PlayerView({
|
||||||
@ -21,11 +23,17 @@ class PlayerView extends ConsumerStatefulWidget {
|
|||||||
ConsumerState<PlayerView> createState() => _PlayerViewState();
|
ConsumerState<PlayerView> createState() => _PlayerViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PlayerViewState extends ConsumerState<PlayerView>
|
class _PlayerViewState extends ConsumerState<PlayerView> {
|
||||||
with AutomaticKeepAliveClientMixin {
|
bool _endScreenOpened = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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);
|
var currentlyPlayingTrack = ref.watch(playerProvider);
|
||||||
if (currentlyPlayingTrack == null) {
|
if (currentlyPlayingTrack == null) {
|
||||||
@ -34,9 +42,6 @@ class _PlayerViewState extends ConsumerState<PlayerView>
|
|||||||
message: StringConstants.unableToLoadAudio,
|
message: StringConstants.unableToLoadAudio,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var coverUrl = currentlyPlayingTrack.coverUrl;
|
|
||||||
var artist = currentlyPlayingTrack.artist;
|
|
||||||
var file = currentlyPlayingTrack.audio.first.files.first;
|
var file = currentlyPlayingTrack.audio.first.files.first;
|
||||||
|
|
||||||
var size = MediaQuery.of(context).size.width;
|
var size = MediaQuery.of(context).size.width;
|
||||||
@ -59,33 +64,46 @@ class _PlayerViewState extends ConsumerState<PlayerView>
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: spacerHeight20),
|
SizedBox(height: spacerHeight20),
|
||||||
OverlayCoverImageWidget(imageUrl: coverUrl),
|
OverlayCoverImageWidget(imageUrl: playbackState.track.imageUrl),
|
||||||
SizedBox(height: spacerHeight48),
|
SizedBox(height: spacerHeight48),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||||
child: ArtistTitleWidget(
|
child: ArtistTitleWidget(
|
||||||
trackTitle: currentlyPlayingTrack.title,
|
trackTitle: playbackState.track.title,
|
||||||
artistName: artist?.name,
|
artistName: playbackState.track.artist,
|
||||||
artistUrlPath: artist?.path,
|
artistUrlPath: playbackState.track.artistUrl,
|
||||||
isPlayerScreen: true,
|
isPlayerScreen: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DurationIndicatorWidget(
|
DurationIndicatorWidget(
|
||||||
file: file,
|
totalDuration: playbackState.duration,
|
||||||
trackId: currentlyPlayingTrack.id,
|
currentPosition: playbackState.position,
|
||||||
|
onSeekEnd: (value) {
|
||||||
|
ref.read(playerProvider.notifier).seekToPosition(value);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: spacerHeight24),
|
SizedBox(height: spacerHeight24),
|
||||||
Transform.translate(
|
Transform.translate(
|
||||||
offset: Offset(0, -10),
|
offset: Offset(0, -10),
|
||||||
child: PlayerButtonsWidget(
|
child: PlayerButtonsWidget(
|
||||||
file: file,
|
isPlaying: playbackState.isPlaying,
|
||||||
trackModel: currentlyPlayingTrack,
|
onPlayPause: () =>
|
||||||
|
ref.read(playerProvider.notifier).playPause(),
|
||||||
|
onSkip10SecondsBackward: () => ref
|
||||||
|
.read(playerProvider.notifier)
|
||||||
|
.skip10SecondsBackward(),
|
||||||
|
onSkip10SecondsForward: () => ref
|
||||||
|
.read(playerProvider.notifier)
|
||||||
|
.skip10SecondsForward(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: spacerHeight24),
|
SizedBox(height: spacerHeight24),
|
||||||
BottomActionWidget(
|
BottomActionWidget(
|
||||||
trackModel: currentlyPlayingTrack,
|
trackModel: currentlyPlayingTrack,
|
||||||
file: file,
|
file: file,
|
||||||
|
onSpeedChanged: (speed) =>
|
||||||
|
ref.read(playerProvider.notifier).setSpeed(speed),
|
||||||
|
isBackgroundSoundSelected: _isBackgroundSoundSelected(),
|
||||||
),
|
),
|
||||||
SizedBox(height: 40),
|
SizedBox(height: 40),
|
||||||
],
|
],
|
||||||
@ -96,15 +114,40 @@ class _PlayerViewState extends ConsumerState<PlayerView>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _handleClose() async {
|
bool _isBackgroundSoundSelected() {
|
||||||
final audioProvider = ref.read(audioPlayerNotifierProvider);
|
var bgSoundNotifier = ref.read(backgroundSoundsNotifierProvider);
|
||||||
await audioProvider.stop();
|
|
||||||
|
|
||||||
|
return bgSoundNotifier.selectedBgSound != null &&
|
||||||
|
bgSoundNotifier.selectedBgSound?.title != StringConstants.none;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _handleClose() async {
|
||||||
|
_resetState();
|
||||||
context.pop();
|
context.pop();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
void _resetState() {
|
||||||
bool get wantKeepAlive => true;
|
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/constants/constants.dart';
|
||||||
import 'package:Medito/routes/routes.dart';
|
import 'package:Medito/routes/routes.dart';
|
||||||
|
import 'package:Medito/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:marquee/marquee.dart';
|
import 'package:marquee/marquee.dart';
|
||||||
@ -17,7 +18,7 @@ class ArtistTitleWidget extends ConsumerWidget {
|
|||||||
this.titleHeight = 35,
|
this.titleHeight = 35,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String trackTitle;
|
final String? trackTitle;
|
||||||
final String? artistName, artistUrlPath;
|
final String? artistName, artistUrlPath;
|
||||||
final double trackTitleFontSize;
|
final double trackTitleFontSize;
|
||||||
final double artistNameFontSize;
|
final double artistNameFontSize;
|
||||||
@ -31,16 +32,20 @@ class ArtistTitleWidget extends ConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_title(context),
|
_title(context),
|
||||||
if (artistName != null) _subtitle(context),
|
if (artistName.isNotNullAndNotEmpty()) _subtitle(context),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _title(BuildContext context) {
|
Widget _title(BuildContext context) {
|
||||||
|
if (trackTitle?.isEmpty == true || trackTitle == null) {
|
||||||
|
return SizedBox(height: titleHeight);
|
||||||
|
}
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: titleHeight,
|
height: titleHeight,
|
||||||
child: Marquee(
|
child: Marquee(
|
||||||
text: trackTitle,
|
text: trackTitle ?? 'Title',
|
||||||
style: Theme.of(context).primaryTextTheme.headlineMedium?.copyWith(
|
style: Theme.of(context).primaryTextTheme.headlineMedium?.copyWith(
|
||||||
fontFamily: SourceSerif,
|
fontFamily: SourceSerif,
|
||||||
color: ColorConstants.walterWhite,
|
color: ColorConstants.walterWhite,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
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/audio_speed_widget.dart';
|
||||||
|
import 'package:Medito/views/player/widgets/bottom_actions/widgets/bg_sound_widget.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'widgets/audio_download_widget.dart';
|
import '../../../../models/track/track_model.dart';
|
||||||
import 'widgets/bg_sound_widget.dart';
|
|
||||||
import 'widgets/mark_favourite_widget.dart';
|
import 'widgets/mark_favourite_widget.dart';
|
||||||
|
|
||||||
class BottomActionWidget extends StatelessWidget {
|
class BottomActionWidget extends StatelessWidget {
|
||||||
@ -12,10 +12,14 @@ class BottomActionWidget extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.trackModel,
|
required this.trackModel,
|
||||||
required this.file,
|
required this.file,
|
||||||
|
required this.isBackgroundSoundSelected,
|
||||||
|
required this.onSpeedChanged,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final bool isBackgroundSoundSelected;
|
||||||
final TrackModel trackModel;
|
final TrackModel trackModel;
|
||||||
final TrackFilesModel file;
|
final TrackFilesModel file;
|
||||||
|
final Function(double) onSpeedChanged;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -35,18 +39,16 @@ class BottomActionWidget extends StatelessWidget {
|
|||||||
if (trackModel.hasBackgroundSound)
|
if (trackModel.hasBackgroundSound)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: BgSoundWidget(
|
child: BgSoundWidget(
|
||||||
|
isBackgroundSoundSelected: isBackgroundSoundSelected,
|
||||||
trackModel: trackModel,
|
trackModel: trackModel,
|
||||||
file: file,
|
file: file,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (trackModel.hasBackgroundSound) width8,
|
if (trackModel.hasBackgroundSound) width8,
|
||||||
Expanded(child: AudioSpeedWidget()),
|
Expanded(child: AudioSpeedWidget(onSpeedChanged: onSpeedChanged)),
|
||||||
width8,
|
width8,
|
||||||
Expanded(
|
Expanded(
|
||||||
child: MarkFavouriteWidget(
|
child: MarkFavouriteWidget(trackModel: trackModel, file: file),
|
||||||
trackModel: trackModel,
|
|
||||||
file: file,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -32,7 +32,7 @@ class AudioDownloadWidget extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (downloadAudioProvider.audioDownloadState[downloadFileKey] ==
|
} else if (downloadAudioProvider.audioDownloadState[downloadFileKey] ==
|
||||||
AUDIO_DOWNLOAD_STATE.DOWNLOADIING) {
|
AUDIO_DOWNLOAD_STATE.DOWNLOADING) {
|
||||||
return showDownloadProgress(downloadAudioProvider, downloadFileKey);
|
return showDownloadProgress(downloadAudioProvider, downloadFileKey);
|
||||||
} else {
|
} else {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
|
@ -1,45 +1,44 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
import 'package:Medito/constants/constants.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
class AudioSpeedWidget extends ConsumerStatefulWidget {
|
class AudioSpeedWidget extends ConsumerStatefulWidget {
|
||||||
const AudioSpeedWidget({super.key});
|
const AudioSpeedWidget({super.key, required this.onSpeedChanged});
|
||||||
|
|
||||||
|
final Function(double) onSpeedChanged;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<AudioSpeedWidget> createState() => _AudioSpeedComponentState();
|
ConsumerState<AudioSpeedWidget> createState() => _AudioSpeedComponentState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AudioSpeedComponentState extends ConsumerState<AudioSpeedWidget> {
|
class _AudioSpeedComponentState extends ConsumerState<AudioSpeedWidget> {
|
||||||
@override
|
String _label = 'x1';
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final _provider = ref.read(audioSpeedProvider);
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
_provider.getAudioTrackSpeedFromPref();
|
|
||||||
ref
|
|
||||||
.read(audioPlayerNotifierProvider)
|
|
||||||
.setTrackAudioSpeed(_provider.audioSpeedModel.speed);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final _provider = ref.watch(audioSpeedProvider);
|
var textColor = _label != StringConstants.x1
|
||||||
var audioSpeedModel = _provider.audioSpeedModel;
|
|
||||||
var textColor = audioSpeedModel.label != StringConstants.x1
|
|
||||||
? ColorConstants.lightPurple
|
? ColorConstants.lightPurple
|
||||||
: ColorConstants.walterWhite;
|
: ColorConstants.walterWhite;
|
||||||
|
ColorConstants.walterWhite;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_provider.setAudioTrackSpeed();
|
if (_label == StringConstants.x06) {
|
||||||
ref
|
_label = StringConstants.x07;
|
||||||
.read(audioPlayerNotifierProvider)
|
} else if (_label == StringConstants.x07) {
|
||||||
.setTrackAudioSpeed(_provider.audioSpeedModel.speed);
|
_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(
|
child: Text(
|
||||||
audioSpeedModel.label,
|
_label,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyMedium
|
.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/constants/constants.dart';
|
||||||
import 'package:Medito/models/models.dart';
|
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../../../../../models/track/track_model.dart';
|
||||||
|
|
||||||
class BgSoundWidget extends ConsumerWidget {
|
class BgSoundWidget extends ConsumerWidget {
|
||||||
const BgSoundWidget({
|
const BgSoundWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.trackModel,
|
required this.trackModel,
|
||||||
required this.file,
|
required this.file,
|
||||||
|
required this.isBackgroundSoundSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
final TrackModel trackModel;
|
final TrackModel trackModel;
|
||||||
final TrackFilesModel file;
|
final TrackFilesModel file;
|
||||||
|
final bool isBackgroundSoundSelected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final selectedBgSound =
|
|
||||||
ref.watch(backgroundSoundsNotifierProvider).selectedBgSound;
|
|
||||||
var checkIsBgSoundSelected = selectedBgSound != null &&
|
|
||||||
selectedBgSound.title != StringConstants.none;
|
|
||||||
|
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.push(
|
context.push(
|
||||||
@ -31,7 +28,7 @@ class BgSoundWidget extends ConsumerWidget {
|
|||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.music_note,
|
Icons.music_note,
|
||||||
color: checkIsBgSoundSelected
|
color: isBackgroundSoundSelected
|
||||||
? ColorConstants.lightPurple
|
? ColorConstants.lightPurple
|
||||||
: ColorConstants.walterWhite,
|
: ColorConstants.walterWhite,
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:Medito/constants/constants.dart';
|
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:Medito/utils/duration_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
@ -10,12 +6,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
class DurationIndicatorWidget extends ConsumerStatefulWidget {
|
class DurationIndicatorWidget extends ConsumerStatefulWidget {
|
||||||
const DurationIndicatorWidget({
|
const DurationIndicatorWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.file,
|
required this.totalDuration,
|
||||||
required this.trackId,
|
required this.currentPosition,
|
||||||
|
required this.onSeekEnd,
|
||||||
});
|
});
|
||||||
|
|
||||||
final TrackFilesModel file;
|
final int totalDuration;
|
||||||
final String trackId;
|
final int currentPosition;
|
||||||
|
final void Function(int) onSeekEnd;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<DurationIndicatorWidget> createState() =>
|
ConsumerState<DurationIndicatorWidget> createState() =>
|
||||||
@ -24,61 +22,23 @@ class DurationIndicatorWidget extends ConsumerStatefulWidget {
|
|||||||
|
|
||||||
class _DurationIndicatorWidgetState
|
class _DurationIndicatorWidgetState
|
||||||
extends ConsumerState<DurationIndicatorWidget> {
|
extends ConsumerState<DurationIndicatorWidget> {
|
||||||
final _minSeconds = 0.0;
|
bool _isSeekbarBeingDragged = false;
|
||||||
double? _dragSeekbarValue;
|
double _dragSeekbarValue = 0;
|
||||||
double _maxDuration = 0.0;
|
|
||||||
bool _draggingSeekbar = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final audioPositionAndPlayerState =
|
return _durationBar(
|
||||||
ref.watch(audioPositionAndPlayerStateProvider);
|
context,
|
||||||
final audioPlayerNotifier = ref.watch(audioPlayerNotifierProvider);
|
// 99 is to avoid rounding errors which would result in the current
|
||||||
|
// position being larger that the total duration
|
||||||
_maxDuration = widget.file.duration.toDouble();
|
widget.totalDuration / 99,
|
||||||
|
widget.currentPosition / 100,
|
||||||
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(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onChangeEnd(
|
Padding _durationBar(BuildContext context,
|
||||||
WidgetRef ref,
|
double totalDuration,
|
||||||
PositionAndPlayerStateState data,
|
double currentDuration,) {
|
||||||
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,
|
|
||||||
) {
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 32, right: 32, top: 0, bottom: 0),
|
padding: const EdgeInsets.only(left: 32, right: 32, top: 0, bottom: 0),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -92,28 +52,27 @@ class _DurationIndicatorWidgetState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Slider(
|
child: Slider(
|
||||||
min: _minSeconds,
|
min: 0.0,
|
||||||
|
max: totalDuration > 0.0 ? totalDuration : 1.0,
|
||||||
activeColor: ColorConstants.walterWhite,
|
activeColor: ColorConstants.walterWhite,
|
||||||
inactiveColor: ColorConstants.onyx,
|
inactiveColor: ColorConstants.onyx,
|
||||||
max: _maxDuration,
|
value:
|
||||||
value: currentDuration.toDouble(),
|
_isSeekbarBeingDragged ? _dragSeekbarValue : currentDuration,
|
||||||
onChanged: (val) {
|
onChanged: (val) {
|
||||||
if (!_draggingSeekbar) {
|
if (!_isSeekbarBeingDragged) {
|
||||||
_draggingSeekbar = true;
|
_isSeekbarBeingDragged = true;
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_dragSeekbarValue = val;
|
_dragSeekbarValue = val;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onChangeEnd: (val) => onChangeEnd(ref, data, val),
|
onChangeEnd: _onChangeEnd,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Transform.translate(
|
Transform.translate(
|
||||||
offset: Offset(0, -14),
|
offset: Offset(0, -14),
|
||||||
child: _durationLabels(
|
child: _durationLabels(
|
||||||
context,
|
context,
|
||||||
currentDuration.round(),
|
|
||||||
_maxDuration.round(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -121,37 +80,42 @@ class _DurationIndicatorWidgetState
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Row _durationLabels(
|
void _onChangeEnd(double val) {
|
||||||
BuildContext context,
|
_isSeekbarBeingDragged = false;
|
||||||
int position,
|
widget.onSeekEnd((val * 100).toInt());
|
||||||
int duration,
|
}
|
||||||
) {
|
|
||||||
|
Row _durationLabels(BuildContext context,) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
_durationLabel(
|
_durationLabel(
|
||||||
context,
|
context,
|
||||||
Duration(milliseconds: position).toMinutesSeconds(),
|
Duration(milliseconds: widget.currentPosition.round())
|
||||||
|
.toMinutesSeconds(),
|
||||||
),
|
),
|
||||||
_durationLabel(
|
_durationLabel(
|
||||||
context,
|
context,
|
||||||
Duration(milliseconds: duration).toMinutesSeconds(),
|
Duration(milliseconds: widget.totalDuration.round())
|
||||||
|
.toMinutesSeconds(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Text _durationLabel(
|
Text _durationLabel(BuildContext context,
|
||||||
BuildContext context,
|
String label,) {
|
||||||
String label,
|
|
||||||
) {
|
|
||||||
return Text(
|
return Text(
|
||||||
label,
|
label,
|
||||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
style: Theme
|
||||||
color: ColorConstants.graphite,
|
.of(context)
|
||||||
fontFamily: DmMono,
|
.textTheme
|
||||||
fontSize: 12,
|
.titleSmall
|
||||||
),
|
?.copyWith(
|
||||||
|
color: ColorConstants.graphite,
|
||||||
|
fontFamily: DmMono,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,19 +146,18 @@ class CustomTrackShape extends RoundedRectSliderTrackShape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(
|
void paint(PaintingContext context,
|
||||||
PaintingContext context,
|
Offset offset, {
|
||||||
Offset offset, {
|
required RenderBox parentBox,
|
||||||
required RenderBox parentBox,
|
required SliderThemeData sliderTheme,
|
||||||
required SliderThemeData sliderTheme,
|
required Animation<double> enableAnimation,
|
||||||
required Animation<double> enableAnimation,
|
required TextDirection textDirection,
|
||||||
required TextDirection textDirection,
|
required Offset thumbCenter,
|
||||||
required Offset thumbCenter,
|
Offset? secondaryOffset,
|
||||||
Offset? secondaryOffset,
|
bool isDiscrete = false,
|
||||||
bool isDiscrete = false,
|
bool isEnabled = false,
|
||||||
bool isEnabled = false,
|
double additionalActiveTrackHeight = 0,
|
||||||
double additionalActiveTrackHeight = 0,
|
}) {
|
||||||
}) {
|
|
||||||
super.paint(
|
super.paint(
|
||||||
context,
|
context,
|
||||||
offset,
|
offset,
|
||||||
|
@ -1,51 +1,40 @@
|
|||||||
import 'package:Medito/constants/constants.dart';
|
import 'package:Medito/constants/constants.dart';
|
||||||
import 'package:Medito/providers/providers.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
class PlayPauseButtonWidget extends ConsumerWidget {
|
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 double iconSize;
|
||||||
|
final bool isPlaying;
|
||||||
|
final Function() onPlayPause;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final provider = ref.watch(audioPlayerNotifierProvider);
|
|
||||||
|
|
||||||
return StreamBuilder<bool>(
|
return InkWell(
|
||||||
stream: provider.playbackState.map((state) => state.playing).distinct(),
|
onTap: onPlayPause,
|
||||||
builder: (context, snapshot) {
|
borderRadius: BorderRadius.circular(iconSize / 2),
|
||||||
final playing = snapshot.data ?? false;
|
child: AnimatedCrossFade(
|
||||||
|
firstChild: Icon(
|
||||||
return InkWell(
|
Icons.play_circle_fill,
|
||||||
onTap: () => _handleTap(ref, playing),
|
size: iconSize,
|
||||||
borderRadius: BorderRadius.circular(iconSize / 2),
|
color: ColorConstants.walterWhite,
|
||||||
child: AnimatedCrossFade(
|
),
|
||||||
firstChild: Icon(
|
secondChild: Icon(
|
||||||
Icons.play_circle_fill,
|
Icons.pause_circle_outlined,
|
||||||
size: iconSize,
|
size: iconSize,
|
||||||
color: ColorConstants.walterWhite,
|
color: ColorConstants.walterWhite,
|
||||||
),
|
),
|
||||||
secondChild: Icon(
|
crossFadeState:
|
||||||
Icons.pause_circle_outlined,
|
isPlaying ? CrossFadeState.showSecond : CrossFadeState.showFirst,
|
||||||
size: iconSize,
|
duration: Duration(milliseconds: 250),
|
||||||
color: ColorConstants.walterWhite,
|
),
|
||||||
),
|
|
||||||
crossFadeState:
|
|
||||||
playing ? CrossFadeState.showSecond : CrossFadeState.showFirst,
|
|
||||||
duration: Duration(milliseconds: 500),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
@ -7,13 +5,17 @@ import 'play_pause_button_widget.dart';
|
|||||||
|
|
||||||
class PlayerButtonsWidget extends ConsumerWidget {
|
class PlayerButtonsWidget extends ConsumerWidget {
|
||||||
const PlayerButtonsWidget({
|
const PlayerButtonsWidget({
|
||||||
|
required this.onSkip10SecondsBackward,
|
||||||
|
required this.onSkip10SecondsForward,
|
||||||
|
required this.isPlaying,
|
||||||
super.key,
|
super.key,
|
||||||
required this.trackModel,
|
required this.onPlayPause,
|
||||||
required this.file,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final TrackFilesModel file;
|
final Function() onSkip10SecondsBackward;
|
||||||
final TrackModel trackModel;
|
final Function() onSkip10SecondsForward;
|
||||||
|
final bool isPlaying;
|
||||||
|
final Function() onPlayPause;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@ -21,19 +23,21 @@ class PlayerButtonsWidget extends ConsumerWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
_rewindButton(ref),
|
_rewindButton(),
|
||||||
SizedBox(width: 32),
|
SizedBox(width: 32),
|
||||||
PlayPauseButtonWidget(),
|
PlayPauseButtonWidget(
|
||||||
|
isPlaying: isPlaying,
|
||||||
|
onPlayPause: onPlayPause,
|
||||||
|
),
|
||||||
SizedBox(width: 32),
|
SizedBox(width: 32),
|
||||||
_forwardButton(ref),
|
_forwardButton(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton _rewindButton(WidgetRef ref) {
|
IconButton _rewindButton() {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () =>
|
onPressed: onSkip10SecondsBackward,
|
||||||
_handleForwardAndRewind(ref, SKIP_AUDIO.SKIP_BACKWARD_10),
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.replay_10_rounded,
|
Icons.replay_10_rounded,
|
||||||
size: 40,
|
size: 40,
|
||||||
@ -41,30 +45,13 @@ class PlayerButtonsWidget extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton _forwardButton(WidgetRef ref) {
|
IconButton _forwardButton() {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () => _handleForwardAndRewind(ref, SKIP_AUDIO.SKIP_FORWARD_10),
|
onPressed: onSkip10SecondsForward,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.forward_10_rounded,
|
Icons.forward_10_rounded,
|
||||||
size: 40,
|
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:flutter_svg/svg.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../providers/auth/initialize_user_provider.dart';
|
||||||
|
|
||||||
class SplashView extends ConsumerStatefulWidget {
|
class SplashView extends ConsumerStatefulWidget {
|
||||||
const SplashView({super.key});
|
const SplashView({super.key});
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../../providers/background_sounds/background_sounds_notifier.dart';
|
||||||
|
|
||||||
class TrackView extends ConsumerStatefulWidget {
|
class TrackView extends ConsumerStatefulWidget {
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
@ -90,6 +92,8 @@ class _TrackViewState extends ConsumerState<TrackView>
|
|||||||
TrackModel trackModel,
|
TrackModel trackModel,
|
||||||
TrackFilesModel file,
|
TrackFilesModel file,
|
||||||
) async {
|
) async {
|
||||||
|
final bgSoundNotifier = ref.read(backgroundSoundsNotifierProvider);
|
||||||
|
bgSoundNotifier.getVolumeFromPref();
|
||||||
await ref.read(playerProvider.notifier).loadSelectedTrack(
|
await ref.read(playerProvider.notifier).loadSelectedTrack(
|
||||||
trackModel: trackModel,
|
trackModel: trackModel,
|
||||||
file: file,
|
file: file,
|
||||||
|
@ -51,7 +51,7 @@ class MeditoAppBarSmall extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Widget getTitleWidget(BuildContext context) {
|
Widget getTitleWidget(BuildContext context) {
|
||||||
return titleWidget == null
|
return titleWidget == null
|
||||||
? Text(title ?? '', style: Theme.of(context).textTheme.displayLarge)
|
? Text(title ?? '', style: Theme.of(context).textTheme.displayLarge)
|
||||||
: Row(children: [titleWidget ?? Container()]);
|
: Row(children: [titleWidget!]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -9,14 +9,14 @@ export 'markdown_widget.dart';
|
|||||||
export 'network_image_widget.dart';
|
export 'network_image_widget.dart';
|
||||||
export 'pack_card_widget.dart';
|
export 'pack_card_widget.dart';
|
||||||
export 'shimmers/background_sounds_shimmer_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/folder_shimmer_widget.dart';
|
||||||
|
export 'shimmers/home/editorial_shimmer_widget.dart';
|
||||||
export 'shimmers/home/header_and_announcement_shimmer_widget.dart';
|
export 'shimmers/home/header_and_announcement_shimmer_widget.dart';
|
||||||
export 'shimmers/home/home_shimmer_widget.dart';
|
export 'shimmers/home/home_shimmer_widget.dart';
|
||||||
export 'shimmers/home/quote_shimmer_widget.dart';
|
export 'shimmers/home/quote_shimmer_widget.dart';
|
||||||
export 'shimmers/home/shortcuts_shimmer_widget.dart';
|
export 'shimmers/home/shortcuts_shimmer_widget.dart';
|
||||||
export 'shimmers/home/tiles_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 'shimmers/track_shimmer_widget.dart';
|
||||||
export 'snackbar_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"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
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:
|
auto_size_text:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -285,10 +253,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.1"
|
version: "1.17.2"
|
||||||
connectivity:
|
connectivity:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -776,31 +744,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.7.1"
|
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:
|
list_counter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -837,18 +780,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.15"
|
version: "0.12.16"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.5.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1041,6 +984,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
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:
|
pin_code_fields:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1338,10 +1289,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_span
|
name: source_span
|
||||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.10.0"
|
||||||
sqflite:
|
sqflite:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1418,26 +1369,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4"
|
sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.24.1"
|
version: "1.24.3"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "0.6.0"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93"
|
sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "0.5.3"
|
||||||
timezone:
|
timezone:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1466,10 +1417,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
|
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.14"
|
version: "6.2.1"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1514,10 +1465,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2
|
sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.19"
|
version: "2.2.0"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1566,6 +1517,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
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:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1586,18 +1545,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
|
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.9"
|
version: "5.1.1"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32_registry
|
name: win32_registry
|
||||||
sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9
|
sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1623,5 +1590,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.0.0 <3.7.0-13.0"
|
dart: ">=3.1.2 <3.7.0-13.0"
|
||||||
flutter: ">=3.10.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.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 3.0.11+30011
|
version: 3.1.0+30012
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.18.0 <3.7.0-13.0"
|
sdk: ">=2.18.0 <3.7.0-13.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
@ -27,13 +27,6 @@ dependencies:
|
|||||||
flutter_markdown: ^0.6.7
|
flutter_markdown: ^0.6.7
|
||||||
shared_preferences: ^2.0.5
|
shared_preferences: ^2.0.5
|
||||||
auto_size_text: ^3.0.0-nullsafety.0
|
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
|
rxdart: ^0.27.1
|
||||||
share: ^2.0.1
|
share: ^2.0.1
|
||||||
flutter_html: ^3.0.0-alpha.2
|
flutter_html: ^3.0.0-alpha.2
|
||||||
@ -67,6 +60,7 @@ dependencies:
|
|||||||
vibration: ^1.7.3
|
vibration: ^1.7.3
|
||||||
reorderables: ^0.6.0
|
reorderables: ^0.6.0
|
||||||
google_api_availability: ^5.0.0
|
google_api_availability: ^5.0.0
|
||||||
|
workmanager: ^0.5.2
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
firebase_core_platform_interface: 4.5.1
|
firebase_core_platform_interface: 4.5.1
|
||||||
@ -74,6 +68,7 @@ dependency_overrides:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
pigeon: ^15.0.2
|
||||||
mocktail: ^0.3.0
|
mocktail: ^0.3.0
|
||||||
build_runner: ^2.3.3
|
build_runner: ^2.3.3
|
||||||
riverpod_generator: ^2.1.0
|
riverpod_generator: ^2.1.0
|
||||||
|
Loading…
Reference in New Issue
Block a user