Merge branch 'develop' into github-apk

This commit is contained in:
Michael Speed 2021-09-16 10:33:16 +02:00 committed by GitHub
commit bd8039f54a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 152 additions and 117 deletions

View File

@ -8,6 +8,7 @@ The app is free, forever: no ads, no spam, no need to sign up or pay.
Medito App is a flutter project available on Android and iOS maintained by the Medito Foundation and the community.
## Download the app
- Play Store: https://play.google.com/store/apps/details?id=meditofoundation.medito
- App Store: https://apps.apple.com/us/app/medito/id1500780518
@ -15,6 +16,13 @@ Medito App is a flutter project available on Android and iOS maintained by the M
NOTE: If you install Medito app using APK file, please make sure to verify that the APK file is signed by Medito Foundation. See [VERIFY_APK](VERIFY_APK.md) for more information.
## Install
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/meditohq/medito-app?label=latest%20version&sort=semver)](https://github.com/meditohq/medito-app/releases)
| Android | iOS |
| :--: | :--: |
| <a href="https://play.google.com/store/apps/details?id=meditofoundation.medito"><img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" alt="Get it on Google Play" height="80"/></a><br/>|<a href="https://apps.apple.com/us/app/medito/id1500780518"><img src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg" alt="Download on the App Store" height="55"/></a> |
## How to use this code
The best way to start is by opening the project with [Android Studio](https://developer.android.com/studio) or [Visual Studio](https://visualstudio.microsoft.com/)

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,14 +1,14 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/mike/Development/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/mike/Projects/medito-app"
export "FLUTTER_TARGET=/Users/mike/Projects/medito-app/lib/main.dart"
export "FLUTTER_ROOT=C:\src\flutter\flutter"
export "FLUTTER_APPLICATION_PATH=C:\Users\Harshad\Desktop\Workspace\medito-app"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=lib\main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_BUILD_NAME=2.0.16"
export "FLUTTER_BUILD_NUMBER=20016"
export "DART_DEFINES=flutter.inspector.structuredErrors%3Dtrue"
export "SYMROOT=${SOURCE_ROOT}/../build\ios"
export "FLUTTER_BUILD_NAME=2.0.24"
export "FLUTTER_BUILD_NUMBER=20024"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TRACK_WIDGET_CREATION=false"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=/Users/mike/Projects/medito-app/.dart_tool/package_config.json"
export "PACKAGE_CONFIG=.packages"

View File

@ -14,6 +14,7 @@ import 'package:just_audio/just_audio.dart';
const fadeDuration = 20;
const PLAY_BG_SOUND = 'play_bg_sound';
const SEND_BG_SOUND = 'send_bg_sound';
const INIT_BG_SOUND = 'init_bg_sound';
const SET_BG_SOUND_VOL = 'set_bg_sound_vol';
/// This task defines logic for playing a list of podcast episodes.
@ -24,11 +25,11 @@ class AudioPlayerTask extends BackgroundAudioTask {
StreamSubscription<PlaybackEvent> _eventSubscription;
var _duration = Duration();
var _currentlyPlayingBGSound;
var _currentlyPlayingBGSound = '';
int get index => _player.currentIndex;
MediaItem mediaItem;
var initialBgVolume = 0.6;
var initialBgVolume = 0.4;
var _updatedStats = false;
@override
@ -132,7 +133,9 @@ class AudioPlayerTask extends BackgroundAudioTask {
@override
Future<void> onPlay() {
_bgPlayer.play();
if(_currentlyPlayingBGSound.isNotEmptyAndNotNull()) {
_bgPlayer.play();
}
return _player.play();
}
@ -156,10 +159,12 @@ class AudioPlayerTask extends BackgroundAudioTask {
await _player.stop();
await _broadcastState();
break;
case INIT_BG_SOUND:
AudioServiceBackground.sendCustomEvent(
{SEND_BG_SOUND: _currentlyPlayingBGSound});
break;
case SEND_BG_SOUND:
if ((params as String).isNotEmptyAndNotNull()) {
_currentlyPlayingBGSound = params;
}
_currentlyPlayingBGSound = params ?? '';
AudioServiceBackground.sendCustomEvent(
{SEND_BG_SOUND: _currentlyPlayingBGSound});
break;

View File

@ -80,4 +80,13 @@ class DownloadsBloc {
list.removeWhere((element) => MediaItem.fromJson(jsonDecode(element)).id == mediaFile.id);
await prefs.setStringList(savedFilesKey, list);
}
// This method is used to save the updated order of the list of downloaded sessions
/// Saves a given `List` of `MediaItem` elements (downloaded sessions)
static Future<void> saveDownloads(List<MediaItem> mediaList) async {
var prefs = await SharedPreferences.getInstance();
var list =
mediaList.map((MediaItem mediaItem) => jsonEncode(mediaItem)).toList();
await prefs.setStringList(savedFilesKey, list);
}
}

View File

@ -2,7 +2,8 @@ const String DOWNLOADS = 'Downloads';
const String DOWNLOAD = 'Download';
const String SOUNDS = 'Sounds';
const String BEGIN = 'Begin';
const String PICK_NARRATOR = 'PICK A NARRATOR & DURATION';
const String PICK_NARRATOR_AND_DURATION = 'Pick a narrator & duration';
const String PICK_DURATION = 'Pick a duration';
const String SHOW_DOWNLOADS = 'Show downloads';
const String FAVOURITES = 'Favouites';
const String EMPTY_DOWNLOADS_MESSAGE =

View File

@ -40,20 +40,29 @@ class _DownloadsListWidgetState extends State<DownloadsListWidget>
hasCloseButton: true,
),
key: scaffoldKey,
body: _downloadList.isEmpty
? _getEmptyWidget()
: _getDownloadList(),
body: _downloadList.isEmpty ? _getEmptyWidget() : _getDownloadList(),
);
}
Widget _getDownloadList() {
return ListView.builder(
padding: EdgeInsets.symmetric(vertical: 8),
itemCount: _downloadList.length,
itemBuilder: (context, i) {
var item = _downloadList[i];
return _getSlidingItem(item, context);
// In order for the Dismissible action still to work on the list items,
// the default ReorderableListView is used (instead of the .builder one)
return ReorderableListView(
padding: EdgeInsets.symmetric(vertical: 8),
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
var reorderedItem = _downloadList.removeAt(oldIndex);
_downloadList.insert(newIndex, reorderedItem);
// To ensure, that the new list order is saved
DownloadsBloc.saveDownloads(_downloadList);
});
},
children:
_downloadList.map((item) => _getSlidingItem(item, context)).toList(),
);
}
Widget _getEmptyWidget() => EmptyStateWidget(
@ -67,6 +76,8 @@ class _DownloadsListWidgetState extends State<DownloadsListWidget>
Widget _getSlidingItem(MediaItem item, BuildContext context) {
return InkWell(
// This (additional) key is required in order for the ReorderableListView to distinguish between the different list items
key: ValueKey(item.id),
onTap: () {
_openPlayer(item, context);
},

View File

@ -24,15 +24,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
class HeaderWidget extends StatelessWidget {
HeaderWidget({Key key,
HeaderWidget({
Key key,
this.primaryColorController,
this.titleController,
this.coverController,
this.backgroundImageController,
this.descriptionController,
this.whiteText = false,
})
: super(key: key);
}) : super(key: key);
final StreamController<String> primaryColorController;
final StreamController<String> titleController;
final StreamController<ApiResponse<String>> coverController;
@ -66,15 +66,11 @@ class HeaderWidget extends StatelessWidget {
children: [
MeditoAppBarWidget(transparent: true),
_getRow(),
Container(height: whiteText ? 0 : 16)
],
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: _getDescriptionWidget(),
)
_getDescriptionWidget(),
],
),
);
@ -123,21 +119,17 @@ class HeaderWidget extends StatelessWidget {
);
}
StreamBuilder<String> _getTitleStream() =>
StreamBuilder<String>(
initialData: '',
stream: titleController.stream,
builder: (context, snapshot) {
return Text(
snapshot.data,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.headline1,
);
});
StreamBuilder<String> _getTitleStream() => StreamBuilder<String>(
initialData: '',
stream: titleController.stream,
builder: (context, snapshot) {
return Text(
snapshot.data,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.headline1,
);
});
StreamBuilder<ApiResponse<String>> _actualImageStream() {
return StreamBuilder<ApiResponse<String>>(
@ -153,7 +145,7 @@ class HeaderWidget extends StatelessWidget {
child: CircularProgressIndicator(
backgroundColor: MeditoColors.transparent,
valueColor:
AlwaysStoppedAnimation<Color>(MeditoColors.darkMoon),
AlwaysStoppedAnimation<Color>(MeditoColors.darkMoon),
)),
);
break;
@ -194,7 +186,8 @@ class HeaderWidget extends StatelessWidget {
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data.isNotEmptyAndNotNull()) {
return Padding(
padding: const EdgeInsets.only(bottom: 20.0),
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
child: Markdown(
data: snapshot?.data ?? '',
onTapLink: _linkTap,
@ -202,20 +195,16 @@ class HeaderWidget extends StatelessWidget {
physics: NeverScrollableScrollPhysics(),
styleSheet: MarkdownStyleSheet.fromTheme(Theme.of(context))
.copyWith(
p: Theme
.of(context)
.textTheme
.subtitle1
.copyWith(
fontSize: 14.0,
color: whiteText
? MeditoColors.walterWhite
: MeditoColors.meditoTextGrey)),
p: Theme.of(context).textTheme.subtitle1.copyWith(
fontSize: 14.0,
color: whiteText
? MeditoColors.walterWhite
: MeditoColors.meditoTextGrey)),
shrinkWrap: true,
),
);
} else {
return Container();
return Container(height: 20);
}
});
}

View File

@ -18,7 +18,7 @@ class SmallShortcutsRowWidget extends StatefulWidget {
class SmallShortcutsRowWidgetState extends State<SmallShortcutsRowWidget> {
final _bloc = ShortcutsBloc();
bool isLandscape = false;
@override
void initState() {
super.initState();
@ -31,46 +31,52 @@ class SmallShortcutsRowWidgetState extends State<SmallShortcutsRowWidget> {
@override
Widget build(BuildContext context) {
return SizeChangedLayoutNotifier(
child: StreamBuilder<ApiResponse<ShortcutsResponse>>(
stream: _bloc.shortcutList.stream,
initialData: ApiResponse.loading(),
builder: (context, snapshot) {
switch (snapshot.data.status) {
case Status.LOADING:
return _getLoadingWidget();
break;
case Status.COMPLETED:
return GridView.count(
crossAxisCount: 2,
padding:
const EdgeInsets.only(left: 12.0, right: 12.0, top: 8.0),
scrollDirection: Axis.vertical,
childAspectRatio: 2.6,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: List.generate(snapshot.data.body?.data?.length ?? 0,
(index) {
return Card(
clipBehavior: Clip.antiAlias,
color: MeditoColors.deepNight,
child: SmallShortcutWidget(
snapshot.data.body.data[index], widget.onTap),
return OrientationBuilder(builder: (context, orientation) {
if (MediaQuery.of(context).size.width > 600) {
isLandscape = true;
} else {
isLandscape = false;
}
return SizeChangedLayoutNotifier(
child: StreamBuilder<ApiResponse<ShortcutsResponse>>(
stream: _bloc.shortcutList.stream,
initialData: ApiResponse.loading(),
builder: (context, snapshot) {
switch (snapshot.data.status) {
case Status.LOADING:
return _getLoadingWidget();
break;
case Status.COMPLETED:
return GridView.count(
crossAxisCount: isLandscape ? 4 : 2,
padding: const EdgeInsets.only(
left: 12.0, right: 12.0, top: 8.0),
scrollDirection: Axis.vertical,
childAspectRatio: 2.6,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: List.generate(
snapshot.data.body?.data?.length ?? 0, (index) {
return Card(
clipBehavior: Clip.antiAlias,
color: MeditoColors.deepNight,
child: SmallShortcutWidget(
snapshot.data.body.data[index], widget.onTap),
);
}),
);
}),
);
break;
case Status.ERROR:
return Icon(Icons.error);
break;
}
return Container();
}),
);
break;
case Status.ERROR:
return Icon(Icons.error);
break;
}
return Container();
}));
});
}
Widget _getLoadingWidget() => GridView.count(
crossAxisCount: 2,
crossAxisCount: isLandscape ? 4 : 2,
padding: const EdgeInsets.only(left: 12.0, right: 12.0, top: 8.0),
scrollDirection: Axis.vertical,
childAspectRatio: 2.6,

View File

@ -26,7 +26,6 @@ import 'package:Medito/widgets/player/position_indicator_widget.dart';
import 'package:audio_service/audio_service.dart';
import 'package:back_button_interceptor/back_button_interceptor.dart';
import 'package:flutter/material.dart';
import 'package:pedantic/pedantic.dart';
import 'package:rxdart/rxdart.dart';
class ChooseBackgroundSoundDialog extends StatefulWidget {
@ -68,7 +67,7 @@ class _ChooseBackgroundSoundDialogState
volume = await retrieveSavedBgVolume();
_dragBgVolumeSubject.add(volume);
await AudioService.customAction(SET_BG_SOUND_VOL, volume / 100);
await AudioService.customAction(SEND_BG_SOUND);
await AudioService.customAction(INIT_BG_SOUND, "");
}
}
@ -80,7 +79,7 @@ class _ChooseBackgroundSoundDialogState
builder: (context, snapshot) {
var currentSounds;
try {
currentSounds = snapshot.data[SEND_BG_SOUND] ?? NONE;
currentSounds = (snapshot.data[SEND_BG_SOUND] as String).isNotEmptyAndNotNull() ? snapshot.data[SEND_BG_SOUND] : NONE;
} catch (e) {
currentSounds = NONE;
}
@ -222,7 +221,7 @@ class _ChooseBackgroundSoundDialogState
);
void _noneSelected() {
AudioService.customAction(SEND_BG_SOUND, NONE);
AudioService.customAction(SEND_BG_SOUND, '');
AudioService.customAction(PLAY_BG_SOUND, '');
addBgSoundSelectionToSharedPrefs('', '');
}

View File

@ -13,6 +13,8 @@ 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:ui';
import 'package:Medito/network/api_response.dart';
import 'package:Medito/network/downloads/downloads_bloc.dart';
import 'package:Medito/network/session_options/session_options_bloc.dart';
@ -114,7 +116,8 @@ class _SessionOptionsScreenState extends State<SessionOptionsScreen> {
style: Theme.of(context)
.textTheme
.subtitle2
.copyWith(color: parseColor(snapshot.data)));
.copyWith(
color: parseColor(snapshot.data)));
}),
)),
)
@ -167,6 +170,9 @@ class _SessionOptionsScreenState extends State<SessionOptionsScreen> {
Widget _buildOptionsPanel(List<VoiceItem> items) {
var childList = <Widget>[];
// To check whether any of the VoiceItems contains a narrator
var containsNarrator =
items.any((voiceItem) => voiceItem.headerValue.isNotEmptyAndNotNull());
items.forEach((value) {
var section = _getListItem(context, value);
@ -186,11 +192,13 @@ class _SessionOptionsScreenState extends State<SessionOptionsScreen> {
Container(height: 20),
Padding(
padding: EdgeInsets.only(left: 16),
child: Text(PICK_NARRATOR.toUpperCase(),
style: Theme.of(context)
.textTheme
.bodyText1
.copyWith(color: MeditoColors.meditoTextGrey))),
child: Text(
containsNarrator
? PICK_NARRATOR_AND_DURATION.toUpperCase()
: PICK_DURATION.toUpperCase(),
style: Theme.of(context).textTheme.bodyText1.copyWith(
color: MeditoColors.meditoTextGrey,
fontWeight: FontWeight.w600))),
Container(height: 12),
Column(children: childList),
])));
@ -276,22 +284,21 @@ class _DownloadPanelWidgetState extends State<DownloadPanelWidget> {
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 16),
padding: EdgeInsets.only(top: 20, bottom: 16, left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(DOWNLOAD.toUpperCase(),
style: Theme.of(context).textTheme.bodyText2),
style: Theme.of(context).textTheme.bodyText1.copyWith(
color: MeditoColors.meditoTextGrey,
fontWeight: FontWeight.w600)),
Container(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(_getDownloadLabel(),
style: Theme.of(context)
.textTheme
.bodyText1
.copyWith(color: MeditoColors.meditoTextGrey)),
style: Theme.of(context).textTheme.bodyText1),
_getTrailing()
],
)

View File

@ -11,7 +11,7 @@ description: A meditation learning tool
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 2.0.23+20023
version: 2.0.24+20024
environment:
sdk: ">=2.6.0 <3.0.0"
@ -27,10 +27,10 @@ dependencies:
shared_preferences: ^2.0.5
cached_network_image: ^3.0.0
auto_size_text: ^3.0.0-nullsafety.0
just_audio: ^0.7.4
just_audio: ^0.9.5
audio_session: ^0.1.0
audio_service: ^0.17.0
rxdart: ^0.26.0
rxdart: ^0.27.1
share: ^2.0.1
firebase_core: ^1.1.0
firebase_messaging: ^10.0.0