mirror of
https://github.com/meditohq/medito-app.git
synced 2024-08-16 15:40:36 +03:00
Start service on when audio starts
This commit is contained in:
parent
add7c941f2
commit
812fb7a2e3
@ -4,7 +4,7 @@
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
|
||||
@ -33,9 +33,9 @@
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="Medito"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<activity
|
||||
|
@ -1,4 +1,5 @@
|
||||
@UnstableApi
|
||||
@file:UnstableApi
|
||||
|
||||
package meditofoundation.medito
|
||||
|
||||
import AudioData
|
||||
@ -32,6 +33,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
class AudioPlayerService : MediaSessionService(), Player.Listener, MeditoAudioServiceApi,
|
||||
MediaSession.Callback {
|
||||
|
||||
@ -74,6 +76,7 @@ class AudioPlayerService : MediaSessionService(), Player.Listener, MeditoAudioSe
|
||||
|
||||
handler.removeCallbacks(positionUpdateRunnable)
|
||||
|
||||
|
||||
primaryPlayer.removeListener(this)
|
||||
backgroundMusicPlayer.removeListener(this)
|
||||
|
||||
@ -85,7 +88,6 @@ class AudioPlayerService : MediaSessionService(), Player.Listener, MeditoAudioSe
|
||||
|
||||
}
|
||||
|
||||
@UnstableApi
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
|
||||
if (playWhenReady && playbackState == Player.STATE_READY) {
|
||||
@ -107,7 +109,10 @@ class AudioPlayerService : MediaSessionService(), Player.Listener, MeditoAudioSe
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
@androidx.annotation.OptIn(UnstableApi::class)
|
||||
override fun stopService(name: Intent?): Boolean {
|
||||
return super.stopService(name)
|
||||
}
|
||||
|
||||
private fun clearNotification() {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
NotificationUtil.setNotification(
|
||||
@ -157,7 +162,6 @@ class AudioPlayerService : MediaSessionService(), Player.Listener, MeditoAudioSe
|
||||
.setSmallIcon(R.drawable.notification_icon_push)
|
||||
.setLargeIcon(artworkBitmap)
|
||||
.setSilent(true)
|
||||
.setOngoing(true)
|
||||
.setStyle(session?.let { MediaStyleNotificationHelper.MediaStyle(it) })
|
||||
|
||||
return builder.build()
|
||||
@ -322,4 +326,4 @@ class AudioPlayerService : MediaSessionService(), Player.Listener, MeditoAudioSe
|
||||
const val NOTIFICATION_ID = 101011
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,26 +1,27 @@
|
||||
package meditofoundation.medito
|
||||
|
||||
import MeditoAndroidAudioServiceManager
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.embedding.engine.FlutterEngineCache
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
@UnstableApi
|
||||
class MainActivity : FlutterActivity(), MeditoAndroidAudioServiceManager {
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
FlutterEngineCache
|
||||
.getInstance()
|
||||
.put(ENGINE_ID, flutterEngine);
|
||||
.put(ENGINE_ID, flutterEngine)
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine)
|
||||
|
||||
startAudioService()
|
||||
MeditoAndroidAudioServiceManager.setUp(flutterEngine.dartExecutor.binaryMessenger, this)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -29,20 +30,6 @@ class MainActivity : FlutterActivity() {
|
||||
createNotificationChannel()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
startAudioService()
|
||||
}
|
||||
|
||||
private fun startAudioService() {
|
||||
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
|
||||
@ -55,4 +42,13 @@ class MainActivity : FlutterActivity() {
|
||||
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
override fun startService() {
|
||||
val intent = Intent(this, AudioPlayerService::class.java)
|
||||
startForegroundService(intent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ENGINE_ID = "medito_flutter_engine"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.enableR8=true
|
||||
android.enableJetifier=true
|
@ -13,13 +13,13 @@ Affero GNU General Public License for more details.
|
||||
You should have received a copy of the Affero GNU General Public License
|
||||
along with Medito App. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/constants/theme/app_theme.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/routes/routes.dart';
|
||||
import 'package:Medito/src/audio_pigeon.g.dart';
|
||||
import 'package:Medito/utils/stats_utils.dart';
|
||||
import 'package:Medito/utils/utils.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -33,6 +33,7 @@ import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'constants/environments/environment_constants.dart';
|
||||
import 'services/notifications/notifications_service.dart';
|
||||
|
||||
final _androidServiceApi = MeditoAndroidAudioServiceManager();
|
||||
var audioStateNotifier = AudioStateNotifier();
|
||||
var currentEnvironment = EnvironmentConstants.stagingEnv;
|
||||
|
||||
@ -82,16 +83,6 @@ class ParentWidget extends ConsumerStatefulWidget {
|
||||
|
||||
class _ParentWidgetState extends ConsumerState<ParentWidget>
|
||||
with WidgetsBindingObserver {
|
||||
AppLifecycleState currentState = AppLifecycleState.resumed;
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
unawaited(updateStatsFromBg(ref));
|
||||
}
|
||||
currentState = state;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:Medito/models/models.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -17,6 +18,7 @@ import '../shared_preference/shared_preference_provider.dart';
|
||||
import 'download/audio_downloader_provider.dart';
|
||||
|
||||
final _api = MeditoAudioServiceApi();
|
||||
final _androidServiceApi = MeditoAndroidAudioServiceManager();
|
||||
|
||||
final playerProvider =
|
||||
StateNotifierProvider<PlayerProvider, TrackModel?>((ref) {
|
||||
@ -72,6 +74,11 @@ class PlayerProvider extends StateNotifier<TrackModel?> {
|
||||
var downloadPath = await ref.read(audioDownloaderProvider).getTrackPath(
|
||||
_constructFileName(track, file),
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
await _androidServiceApi.startService();
|
||||
// wait half a sec for the service to start
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
}
|
||||
await _api.playAudio(
|
||||
AudioData(
|
||||
url: downloadPath ?? file.path,
|
||||
@ -161,10 +168,12 @@ class PlayerProvider extends StateNotifier<TrackModel?> {
|
||||
fileGuide: guide,
|
||||
timestamp: DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
ref.read(audioStartedEventProvider(
|
||||
event: audio.toJson(),
|
||||
trackId: trackId,
|
||||
));
|
||||
ref.read(
|
||||
audioStartedEventProvider(
|
||||
event: audio.toJson(),
|
||||
trackId: trackId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> seekToPosition(int position) async {
|
||||
|
@ -1,101 +0,0 @@
|
||||
/*This file is part of Medito App.
|
||||
|
||||
Medito App is free software: you can redistribute it and/or modify
|
||||
it under the terms of the Affero GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Medito App is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
Affero GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the Affero GNU General Public License
|
||||
along with Medito App. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
const TILES_ID = 'tiles';
|
||||
const AUDIO_SET = 'audio-set';
|
||||
const AUDIO_DATA = 'audio-data';
|
||||
const ATTRIBUTIONS = 'attrs';
|
||||
const TEXT = 'text';
|
||||
|
||||
Future<void> clearStorage() async {
|
||||
if (!kIsWeb) {
|
||||
final cacheDir = await getApplicationDocumentsDirectory();
|
||||
|
||||
await cacheDir.list(recursive: true).forEach((element) {
|
||||
try {
|
||||
if (element.path.endsWith('txt') || element.path.endsWith('mp3')) {
|
||||
element.deleteSync(recursive: true);
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> get _localPath async {
|
||||
if (kIsWeb) return '';
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
|
||||
return directory.path;
|
||||
}
|
||||
|
||||
Future<File> _localFile(String name) async {
|
||||
final path = await _localPath;
|
||||
|
||||
return File('$path/$name.txt');
|
||||
}
|
||||
|
||||
Future<String?> _readCache(String id) async {
|
||||
id = id.replaceAll('/', '+');
|
||||
|
||||
final file = await _localFile(id);
|
||||
|
||||
var lastModified;
|
||||
try {
|
||||
lastModified = await file.lastModified();
|
||||
} on FileSystemException {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lastModified.add(Duration(days: 1)).isBefore(DateTime.now())) return null;
|
||||
|
||||
// Read the file.
|
||||
return await file.readAsString();
|
||||
}
|
||||
|
||||
String? encoded(obj) {
|
||||
return obj != null ? json.encode(obj) : null;
|
||||
}
|
||||
|
||||
//ignore:avoid-dynamic
|
||||
dynamic decoded(String obj) {
|
||||
return json.decode(obj);
|
||||
}
|
||||
|
||||
Future<File?> writeJSONToCache(String? body, String id) async {
|
||||
if (body != null) {
|
||||
id = id.replaceAll('/', '+');
|
||||
final file = await _localFile(id);
|
||||
|
||||
return file.writeAsString('$body');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String?> readJSONFromCache(String url) async {
|
||||
try {
|
||||
return await _readCache(url);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,282 +0,0 @@
|
||||
/*This file is part of Medito App.
|
||||
|
||||
Medito App is free software: you can redistribute it and/or modify
|
||||
it under the terms of the Affero GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Medito App is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
Affero GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the Affero GNU General Public License
|
||||
along with Medito App. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
|
||||
import 'package:Medito/constants/constants.dart';
|
||||
import 'package:Medito/providers/providers.dart';
|
||||
import 'package:Medito/utils/cache.dart';
|
||||
import 'package:Medito/utils/utils.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
//ignore: prefer-match-file-name
|
||||
enum UnitType { day, min, tracks }
|
||||
|
||||
String getUnits(UnitType type, int _) {
|
||||
switch (type) {
|
||||
case UnitType.day:
|
||||
return 'd';
|
||||
case UnitType.min:
|
||||
return '';
|
||||
case UnitType.tracks:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getCurrentStreak() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var streak = prefs.getInt(SharedPreferenceConstants.streakCount) ?? 0;
|
||||
var streakList = await getStreakList();
|
||||
|
||||
if (streakList.isNotEmpty) {
|
||||
var lastDayInStreak =
|
||||
DateTime.fromMillisecondsSinceEpoch(int.parse(streakList.last));
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
if (longerThanOneDayAgo(lastDayInStreak, now)) {
|
||||
streak = 0;
|
||||
await prefs.setInt(SharedPreferenceConstants.streakCount, streak);
|
||||
}
|
||||
}
|
||||
|
||||
return streak.toString();
|
||||
}
|
||||
|
||||
Future<List<String>> getStreakList() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
return prefs.getStringList(SharedPreferenceConstants.streakList) ?? [];
|
||||
}
|
||||
|
||||
Future<bool> setStreakList(List<String> streakList) async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
return prefs.setStringList(SharedPreferenceConstants.streakList, streakList);
|
||||
}
|
||||
|
||||
Future<int> _getCurrentStreakInt() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var streak = prefs.getInt(SharedPreferenceConstants.streakCount);
|
||||
|
||||
return streak ?? 0;
|
||||
}
|
||||
|
||||
Future<bool> updateMinuteCounter(int additionalSecs) async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var current = await _getSecondsListened();
|
||||
var plusOne = current + additionalSecs;
|
||||
await prefs.setInt(SharedPreferenceConstants.secsListened, plusOne);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> updateStreak({String streak = ''}) async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
if (streak.isNotEmpty) {
|
||||
await prefs.setInt(
|
||||
SharedPreferenceConstants.streakCount,
|
||||
int.parse(streak),
|
||||
);
|
||||
await _updateLongestStreak(int.parse(streak), prefs);
|
||||
await addPhantomTrackToStreakList();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var streakList = await getStreakList();
|
||||
var streakCount = prefs.getInt(SharedPreferenceConstants.streakCount) ?? 0;
|
||||
|
||||
if (streakList.isNotEmpty) {
|
||||
//if you have meditated before, was it on today? if not, increase counter
|
||||
final lastDayInStreak =
|
||||
DateTime.fromMillisecondsSinceEpoch(int.parse(streakList.last));
|
||||
final now = DateTime.now();
|
||||
|
||||
if (!isSameDay(lastDayInStreak, now)) {
|
||||
await incrementStreakCounter(streakCount);
|
||||
}
|
||||
} else {
|
||||
//if you've never done one before
|
||||
await incrementStreakCounter(streakCount);
|
||||
}
|
||||
|
||||
streakList.add(DateTime.now().millisecondsSinceEpoch.toString());
|
||||
await setStreakList(streakList);
|
||||
}
|
||||
|
||||
Future<void> addPhantomTrackToStreakList() async {
|
||||
//add this track to the streak list to stop it resetting to 0,
|
||||
// but keep a note of it in fakeStreakList
|
||||
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
var streakList = await getStreakList();
|
||||
var fakeStreakList =
|
||||
prefs.getStringList(SharedPreferenceConstants.fakeStreakList) ?? [];
|
||||
var streakTime = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
streakList.add(streakTime);
|
||||
fakeStreakList.add(streakTime);
|
||||
await setStreakList(streakList);
|
||||
await prefs.setStringList(
|
||||
SharedPreferenceConstants.fakeStreakList,
|
||||
fakeStreakList,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> incrementStreakCounter(
|
||||
int streakCount,
|
||||
) async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
streakCount++;
|
||||
await prefs.setInt(SharedPreferenceConstants.streakCount, streakCount);
|
||||
|
||||
//update longestStreak
|
||||
await _updateLongestStreak(streakCount, prefs);
|
||||
}
|
||||
|
||||
Future _updateLongestStreak(int streakCount, SharedPreferences prefs) async {
|
||||
//update longestStreak
|
||||
var longest = await _getLongestStreakInt();
|
||||
if (streakCount > longest) {
|
||||
await prefs.setInt(SharedPreferenceConstants.longestStreak, streakCount);
|
||||
}
|
||||
}
|
||||
|
||||
void setLongestStreakToCurrentStreak() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
var current = await _getCurrentStreakInt();
|
||||
await prefs.setInt(SharedPreferenceConstants.longestStreak, current);
|
||||
}
|
||||
|
||||
Future<String> getMinutesListened() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var streak = prefs.getInt(SharedPreferenceConstants.secsListened);
|
||||
|
||||
return streak == null ? '0' : Duration(seconds: streak).inMinutes.toString();
|
||||
}
|
||||
|
||||
Future<int> _getSecondsListened() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var streak = prefs.getInt(SharedPreferenceConstants.secsListened);
|
||||
|
||||
return streak ?? 0;
|
||||
}
|
||||
|
||||
Future<String> getLongestStreak() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
if (await _getLongestStreakInt() < await _getCurrentStreakInt()) {
|
||||
return getCurrentStreak();
|
||||
}
|
||||
|
||||
var streak = prefs.getInt(SharedPreferenceConstants.longestStreak);
|
||||
|
||||
return streak == null ? '0' : streak.toString();
|
||||
}
|
||||
|
||||
Future<int> _getLongestStreakInt() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var streak = prefs.getInt(SharedPreferenceConstants.longestStreak);
|
||||
|
||||
return streak ?? 0;
|
||||
}
|
||||
|
||||
Future<String> getNumTracks() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var streak = prefs.getInt(SharedPreferenceConstants.numSessions);
|
||||
|
||||
return streak == null ? '0' : streak.toString();
|
||||
}
|
||||
|
||||
Future<int> getNumTracksInt() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var streak = prefs.getInt(SharedPreferenceConstants.numSessions);
|
||||
|
||||
return streak ?? 0;
|
||||
}
|
||||
|
||||
Future<int> incrementNumTracks() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
var current = await getNumTracksInt();
|
||||
current++;
|
||||
await prefs.setInt(SharedPreferenceConstants.numSessions, current);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
void markAsListened(WidgetRef ref, String id) {
|
||||
unawaited(ref
|
||||
.read(sharedPreferencesProvider)
|
||||
.setBool(SharedPreferenceConstants.listened + id, true));
|
||||
}
|
||||
|
||||
Future<void> clearBgStats() {
|
||||
return writeJSONToCache('', SharedPreferenceConstants.stats);
|
||||
}
|
||||
|
||||
void setVersionCopySeen(int id) async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setInt(SharedPreferenceConstants.copy, id);
|
||||
}
|
||||
|
||||
Future<int> getVersionCopyInt() async {
|
||||
var prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var version = prefs.getInt(SharedPreferenceConstants.copy);
|
||||
|
||||
return version ?? -1;
|
||||
}
|
||||
|
||||
bool isSameDay(DateTime day1, DateTime day2) {
|
||||
return day1.year == day2.year &&
|
||||
day1.month == day2.month &&
|
||||
day1.day == day2.day;
|
||||
}
|
||||
|
||||
bool longerThanOneDayAgo(DateTime lastDayInStreak, DateTime now) {
|
||||
var thirtyTwoHoursAfterTime = DateTime.fromMillisecondsSinceEpoch(
|
||||
lastDayInStreak.millisecondsSinceEpoch + 115200000,
|
||||
);
|
||||
|
||||
return now.isAfter(thirtyTwoHoursAfterTime);
|
||||
}
|
||||
|
||||
Future updateStatsFromBg(WidgetRef ref) async {
|
||||
var read = await readJSONFromCache(SharedPreferenceConstants.stats);
|
||||
|
||||
if (read.isNotNullAndNotEmpty()) {
|
||||
var map = decoded(read!);
|
||||
var id = map[SharedPreferenceConstants.id];
|
||||
var secsListened = map[SharedPreferenceConstants.secsListened];
|
||||
|
||||
if (map != null && map.isNotEmpty) {
|
||||
await updateStreak();
|
||||
await incrementNumTracks();
|
||||
markAsListened(ref, id);
|
||||
await updateMinuteCounter(Duration(seconds: secsListened).inSeconds);
|
||||
}
|
||||
await clearBgStats();
|
||||
}
|
||||
}
|
@ -58,12 +58,6 @@ void createSnackBar(
|
||||
}
|
||||
}
|
||||
|
||||
bool isDayBefore(DateTime day1, DateTime day2) {
|
||||
return day1.year == day2.year &&
|
||||
day1.month == day2.month &&
|
||||
day1.day == day2.day - 1;
|
||||
}
|
||||
|
||||
Future<void> launchURLInBrowser(String url) async {
|
||||
try {
|
||||
final uri = Uri.parse(url);
|
||||
@ -184,12 +178,6 @@ double getBottomPaddingWithStickyMiniPlayer(BuildContext context) {
|
||||
return totalPadding;
|
||||
}
|
||||
|
||||
Future<String> getFilePathForOldAppDownloadedFiles(String mediaItemId) async {
|
||||
var dir = (await getApplicationSupportDirectory()).path;
|
||||
|
||||
return '$dir/${mediaItemId.replaceAll('/', '_').replaceAll(' ', '_')}.mp3';
|
||||
}
|
||||
|
||||
int formatIcon(String icon) {
|
||||
if (icon.isEmpty) return 0;
|
||||
|
||||
|
@ -41,7 +41,11 @@ class JoinWelcomeView extends ConsumerWidget {
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 24, bottom: 16, left: 16, right: 16),
|
||||
top: 24,
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -14,7 +14,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../providers/background_sounds/background_sounds_notifier.dart';
|
||||
import '../../providers/home/home_provider.dart';
|
||||
|
||||
class DownloadsView extends ConsumerStatefulWidget {
|
||||
@override
|
||||
|
@ -5,7 +5,6 @@ import 'package:Medito/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../../providers/me/me_provider.dart';
|
||||
import '../share_btn/share_btn_widget.dart';
|
||||
|
||||
class DebugBottomSheetWidget extends ConsumerWidget {
|
||||
|
@ -26,6 +26,11 @@ class AudioData {
|
||||
Track track;
|
||||
}
|
||||
|
||||
@HostApi()
|
||||
abstract class MeditoAndroidAudioServiceManager {
|
||||
void startService();
|
||||
}
|
||||
|
||||
@HostApi()
|
||||
abstract class MeditoAudioServiceApi {
|
||||
bool playAudio(AudioData audioData);
|
||||
|
Loading…
Reference in New Issue
Block a user