diff --git a/README.md b/README.md
index db95559cb..c7c815398 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
A mobile video player tailored for Japanese language learners.
Latest GitHub Release:
-0.13.0-beta 🇯🇵 → 🇬🇧
+0.13.1-beta 🇯🇵 → 🇬🇧
0.5.3-beta 🇬🇧 → 🇯🇵
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 79c4b3c00..65d32a44b 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -49,6 +49,7 @@ android {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
+ shrinkResources false
}
}
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index a0e9f5e0b..29b9d40b6 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,7 +6,8 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
currentSubtitle;
final ValueNotifier currentDictionaryEntry;
final ValueNotifier currentSubTrack;
+ final ValueNotifier wasPlaying;
final VoidCallback playExternalSubtitles;
final VoidCallback retimeSubtitles;
final YouTubeMux streamData;
diff --git a/chewie/lib/src/material_controls.dart b/chewie/lib/src/material_controls.dart
index 0040c25ca..457f1a574 100755
--- a/chewie/lib/src/material_controls.dart
+++ b/chewie/lib/src/material_controls.dart
@@ -250,6 +250,43 @@ class _MaterialControlsState extends State
);
}
+ Future openExtraShare() async {
+ final chosenOption = await showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ useRootNavigator: true,
+ builder: (context) => const _MoreOptionsDialog(options: [
+ "Search Current Subtitle with Jisho.org",
+ "Translate Current Subtitle with DeepL",
+ "Translate Current Subtitle with Google Translate",
+ "Share Current Subtitle with Menu",
+ ], icons: [
+ Icons.menu_book_rounded,
+ Icons.translate_rounded,
+ Icons.g_translate_rounded,
+ Icons.share_outlined,
+ ]),
+ );
+
+ final String subtitleText = chewieController.currentSubtitle.value.text;
+
+ switch (chosenOption) {
+ case 0:
+ await launch("https://jisho.org/search/$subtitleText");
+ break;
+ case 1:
+ await launch("https://www.deepl.com/translator#ja/en/$subtitleText");
+ break;
+ case 2:
+ await launch(
+ "https://translate.google.com/?sl=ja&tl=en&text=$subtitleText&op=translate");
+ break;
+ case 3:
+ Share.share(subtitleText);
+ break;
+ }
+ }
+
Widget _buildMoreButton(VlcPlayerController controller) {
return GestureDetector(
onTap: () async {
@@ -260,62 +297,53 @@ class _MaterialControlsState extends State
isScrollControlled: true,
useRootNavigator: true,
builder: (context) => _MoreOptionsDialog(options: [
- "Search Current Subtitle with Jisho.org",
- "Translate Current Subtitle with DeepL",
- "Translate Current Subtitle with Google Translate",
- "Share Current Subtitle to App",
- if (gIsSelectMode.value)
+ "Share Current Subtitle to Applications",
+ if (getSelectMode())
"Use Tap to Select Subtitle Selection"
else
"Use Drag to Select Subtitle Selection",
+ if (getFocusMode())
+ "Turn Off Definition Focus Mode"
+ else
+ "Turn On Definition Focus Mode",
"Adjust Subtitle Delay",
"Load External Subtitles",
"Export Current Context to Anki",
], icons: [
- Icons.menu_book_rounded,
- Icons.translate_rounded,
- Icons.g_translate_rounded,
Icons.share_outlined,
- if (gIsSelectMode.value)
+ if (getSelectMode())
Icons.touch_app_rounded
else
Icons.select_all_rounded,
- Icons.timer_sharp,
+ if (getFocusMode())
+ Icons.lightbulb_outline_rounded
+ else
+ Icons.lightbulb,
+ Icons.timer_rounded,
Icons.subtitles_outlined,
Icons.mobile_screen_share_rounded,
]),
);
- final String subtitleText = chewieController.currentSubtitle.value.text;
-
switch (chosenOption) {
case 0:
- await launch("https://jisho.org/search/$subtitleText");
+ openExtraShare();
break;
case 1:
- await launch(
- "https://www.deepl.com/translator#ja/en/$subtitleText");
+ toggleSelectMode();
+ gIsSelectMode.value = getSelectMode();
break;
case 2:
- await launch(
- "https://translate.google.com/?sl=ja&tl=en&text=$subtitleText&op=translate");
+ toggleFocusMode();
break;
case 3:
- Share.share(subtitleText);
- break;
- case 4:
- toggleSelectMode();
- gIsSelectMode.value = getSelectMode();
- break;
- case 5:
controller.pause();
chewieController.retimeSubtitles();
break;
- case 6:
+ case 4:
chewieController.playExternalSubtitles();
break;
- case 7:
- final bool wasPlaying = await controller.isPlaying();
+ case 5:
controller.pause();
final Subtitle currentSubtitle =
@@ -332,7 +360,7 @@ class _MaterialControlsState extends State
chewieController.clipboard,
currentSubtitle,
currentDictionaryEntry,
- wasPlaying,
+ chewieController.wasPlaying.value,
);
break;
@@ -591,6 +619,7 @@ class _MaterialControlsState extends State
void _playPause() {
final isFinished = controller.value.isEnded;
+ chewieController.wasPlaying.value = false;
setState(() {
if (controller.value.isPlaying) {
diff --git a/lib/anki.dart b/lib/anki.dart
index 53d9ee8ac..acbfeab13 100644
--- a/lib/anki.dart
+++ b/lib/anki.dart
@@ -220,9 +220,12 @@ void showAnkiDialog(
flex: 30,
child: SingleChildScrollView(
child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
- Image.file(File(getPreviewImagePath()),
- fit: BoxFit.contain),
+ Image.file(
+ File(getPreviewImagePath()),
+ fit: BoxFit.fitWidth,
+ ),
DeckDropDown(
decks: decks,
selectedDeck: _selectedDeck,
diff --git a/lib/globals.dart b/lib/globals.dart
index c70abe43b..ce5182ab6 100644
--- a/lib/globals.dart
+++ b/lib/globals.dart
@@ -1,5 +1,6 @@
import 'package:async/async.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_vlc_player/flutter_vlc_player.dart';
import 'package:fuzzy/fuzzy.dart';
import 'package:mecab_dart/mecab_dart.dart';
import 'package:package_info/package_info.dart';
@@ -29,3 +30,5 @@ Map gMetadataCache = {};
List gCustomDictionary;
Fuzzy gCustomDictionaryFuzzy;
+
+ValueNotifier gPlayPause = ValueNotifier(true);
diff --git a/lib/main.dart b/lib/main.dart
index 7bbc617f4..6dd24168c 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,8 +1,10 @@
import 'dart:io';
import 'package:async/async.dart';
+import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
+import 'package:flutter_vlc_player/flutter_vlc_player.dart';
import 'package:fuzzy/fuzzy.dart';
import 'package:lazy_load_scrollview/lazy_load_scrollview.dart';
@@ -29,7 +31,8 @@ typedef void SearchCallback(String term);
void main() async {
WidgetsFlutterBinding.ensureInitialized();
- SystemChrome.setEnabledSystemUIOverlays([]);
+ SystemChrome.setEnabledSystemUIOverlays(
+ [SystemUiOverlay.bottom, SystemUiOverlay.top]);
await Permission.storage.request();
requestAnkiDroidPermissions();
@@ -47,9 +50,51 @@ void main() async {
gCustomDictionary = importCustomDictionary();
gCustomDictionaryFuzzy = Fuzzy(getAllImportedWords());
+ await AudioService.connect();
+ await AudioService.start(backgroundTaskEntrypoint: _backgroundTaskEntrypoint);
+
runApp(App());
}
+_backgroundTaskEntrypoint() {
+ AudioServiceBackground.run(() => AudioPlayerTask());
+}
+
+class AudioPlayerTask extends BackgroundAudioTask {
+ AudioPlayerTask();
+
+ @override
+ Future onPlay() async {
+ AudioServiceBackground.sendCustomEvent("playPause");
+ }
+
+ @override
+ Future onPause() async {
+ AudioServiceBackground.sendCustomEvent("playPause");
+ }
+
+ @override
+ Future onFastForward() async {
+ AudioServiceBackground.sendCustomEvent("rewindFastForward");
+ }
+
+ @override
+ Future onRewind() async {
+ AudioServiceBackground.sendCustomEvent("rewindFastForward");
+ }
+}
+
+void unlockLandscape() {
+ SystemChrome.setPreferredOrientations([
+ DeviceOrientation.portraitUp,
+ DeviceOrientation.landscapeLeft,
+ DeviceOrientation.landscapeRight,
+ ]);
+
+ SystemChrome.setEnabledSystemUIOverlays(
+ [SystemUiOverlay.bottom, SystemUiOverlay.top]);
+}
+
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
@@ -63,7 +108,7 @@ class App extends StatelessWidget {
appBarTheme: AppBarTheme(backgroundColor: Colors.black),
canvasColor: Colors.grey[900],
),
- home: Home(),
+ home: AudioServiceWidget(child: Home()),
);
}
}
@@ -97,13 +142,7 @@ class _HomeState extends State {
builder: (context) => Player(),
),
).then((returnValue) {
- setState(() {
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.portraitUp,
- DeviceOrientation.landscapeLeft,
- DeviceOrientation.landscapeRight,
- ]);
- });
+ unlockLandscape();
});
} else {
_selectedIndex = index;
@@ -177,11 +216,7 @@ class _HomeState extends State {
@override
Widget build(BuildContext context) {
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.portraitUp,
- DeviceOrientation.landscapeLeft,
- DeviceOrientation.landscapeRight,
- ]);
+ unlockLandscape();
return new WillPopScope(
onWillPop: _onWillPop,
@@ -716,11 +751,7 @@ class _HomeState extends State {
),
).then((returnValue) {
setState(() {
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.portraitUp,
- DeviceOrientation.landscapeLeft,
- DeviceOrientation.landscapeRight,
- ]);
+ unlockLandscape();
});
setLastPlayedPath(webURL);
@@ -889,11 +920,7 @@ class _HomeState extends State {
),
),
).then((returnValue) {
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.portraitUp,
- DeviceOrientation.landscapeLeft,
- DeviceOrientation.landscapeRight,
- ]);
+ unlockLandscape();
});
},
);
@@ -1145,11 +1172,7 @@ class _YouTubeResultState extends State
),
),
).then((returnValue) {
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.portraitUp,
- DeviceOrientation.landscapeLeft,
- DeviceOrientation.landscapeRight,
- ]);
+ unlockLandscape();
setLastPlayedPath(videoStreamURL);
setLastPlayedPosition(0);
@@ -1759,11 +1782,7 @@ class _HistoryResultState extends State
),
),
).then((returnValue) {
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.portraitUp,
- DeviceOrientation.landscapeLeft,
- DeviceOrientation.landscapeRight,
- ]);
+ unlockLandscape();
setLastPlayedPath(history.url);
setLastPlayedPosition(0);
diff --git a/lib/player.dart b/lib/player.dart
index d990caa35..16f9a9716 100644
--- a/lib/player.dart
+++ b/lib/player.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
+import 'package:audio_service/audio_service.dart';
import 'package:chewie/chewie.dart';
import 'package:clipboard_monitor/clipboard_monitor.dart';
import 'package:external_app_launcher/external_app_launcher.dart';
@@ -33,6 +34,15 @@ class Player extends StatelessWidget {
final String url;
final Video video;
+ void lockLandscape() {
+ SystemChrome.setPreferredOrientations([
+ DeviceOrientation.landscapeLeft,
+ DeviceOrientation.landscapeRight,
+ ]);
+
+ SystemChrome.setEnabledSystemUIOverlays([]);
+ }
+
@override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
@@ -104,10 +114,7 @@ class Player extends StatelessWidget {
setLastPlayedPosition(0);
gIsResumable.value = getResumeAvailable();
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.landscapeLeft,
- DeviceOrientation.landscapeRight,
- ]);
+ lockLandscape();
VideoHistory history = VideoHistory(
videoFile.path,
@@ -168,10 +175,7 @@ class Player extends StatelessWidget {
internalSubs = extractWebSubtitle(webSubtitles);
}
- SystemChrome.setPreferredOrientations([
- DeviceOrientation.landscapeLeft,
- DeviceOrientation.landscapeRight,
- ]);
+ lockLandscape();
VideoHistory history = VideoHistory(
url,
@@ -260,6 +264,7 @@ class _VideoPlayerState extends State {
String _volatileText = "";
FocusNode _subtitleFocusNode = new FocusNode();
bool networkNotSet = true;
+ ValueNotifier _wasPlaying = ValueNotifier(false);
Timer timer;
@@ -270,6 +275,31 @@ class _VideoPlayerState extends State {
Duration(seconds: 1), (Timer t) => updateDurationOrSeek());
}
+ Future playPause() async {
+ _wasPlaying.value = false;
+
+ if (getVideoPlayerController().value.isPlaying) {
+ await getVideoPlayerController().pause();
+ } else {
+ await getVideoPlayerController().play();
+ }
+ }
+
+ Future rewindFastForward() async {
+ await getVideoPlayerController().seekTo(_currentSubtitle.value.startTime);
+ }
+
+ @override
+ void dispose() {
+ if (_videoPlayerController != null && _chewieController != null) {
+ _videoPlayerController?.stopRendererScanning();
+ _videoPlayerController?.dispose();
+ _chewieController?.dispose();
+ }
+ timer.cancel();
+ super.dispose();
+ }
+
void startClipboardMonitor() {
ClipboardMonitor.registerCallback(onClipboardText);
}
@@ -349,17 +379,6 @@ class _VideoPlayerState extends State {
);
final _currentSubTrack = ValueNotifier(-1);
- @override
- void dispose() {
- super.dispose();
- if (_videoPlayerController != null && _chewieController != null) {
- _videoPlayerController?.stopRendererScanning();
- _videoPlayerController?.dispose();
- _chewieController?.dispose();
- }
- timer.cancel();
- }
-
@override
Widget build(BuildContext context) {
startClipboardMonitor();
@@ -375,7 +394,7 @@ class _VideoPlayerState extends State {
onHorizontalDragUpdate: (details) {
if (details.delta.dx > 20) {
getVideoPlayerController()
- .seekTo(_currentSubtitle.value.endTime);
+ .seekTo(_currentSubtitle.value.startTime);
} else if (details.delta.dx < -20) {
getVideoPlayerController()
.seekTo(_currentSubtitle.value.startTime);
@@ -392,6 +411,22 @@ class _VideoPlayerState extends State {
},
child: getSubtitleWrapper(),
),
+ StreamBuilder(
+ stream: AudioService.customEventStream,
+ builder: (context, snapshot) {
+ String response = snapshot.data;
+ switch (response) {
+ case "playPause":
+ playPause();
+ break;
+ case "rewindFastForward":
+ rewindFastForward();
+ break;
+ default:
+ }
+ return Container();
+ },
+ ),
buildSubTrackChanger(),
buildDictionary(),
],
@@ -400,6 +435,20 @@ class _VideoPlayerState extends State {
);
}
+ void exportLongCallback(
+ Subtitle selectedSubtitle,
+ ) {
+ exportToAnki(
+ context,
+ getChewieController(),
+ getVideoPlayerController(),
+ _clipboard,
+ selectedSubtitle,
+ _currentDictionaryEntry.value,
+ false,
+ );
+ }
+
Future _onWillPop() async {
Widget alertDialog = AlertDialog(
shape: RoundedRectangleBorder(
@@ -486,6 +535,7 @@ class _VideoPlayerState extends State {
currentDictionaryEntry: _currentDictionaryEntry,
currentSubtitle: _currentSubtitle,
currentSubTrack: _currentSubTrack,
+ wasPlaying: _wasPlaying,
playExternalSubtitles: playExternalSubtitles,
retimeSubtitles: retimeSubtitles,
streamData: streamData,
@@ -803,6 +853,11 @@ class _VideoPlayerState extends State {
padding: EdgeInsets.all(16.0),
child: InkWell(
onTap: () {
+ if (getFocusMode() && _wasPlaying.value) {
+ _videoPlayerController.play();
+ _wasPlaying.value = false;
+ }
+
_clipboard.value = "";
_currentDictionaryEntry.value = DictionaryEntry(
word: "",
@@ -837,6 +892,11 @@ class _VideoPlayerState extends State {
alignment: Alignment.topCenter,
child: GestureDetector(
onTap: () {
+ if (getFocusMode() && _wasPlaying.value) {
+ _videoPlayerController.play();
+ _wasPlaying.value = false;
+ }
+
_clipboard.value = "";
_currentDictionaryEntry.value = DictionaryEntry(
word: "",
@@ -972,8 +1032,15 @@ class _VideoPlayerState extends State {
if (_clipboard.value == "") {
return Container();
}
+
switch (snapshot.connectionState) {
case ConnectionState.waiting:
+ if (getFocusMode()) {
+ _wasPlaying.value =
+ getVideoPlayerController().value.isPlaying;
+ _videoPlayerController.pause();
+ }
+
return buildDictionaryLoading(clipboard);
default:
List entries = snapshot.data;
@@ -1067,7 +1134,7 @@ class _VideoPlayerState extends State {
}
}
- showModalBottomSheet(
+ await showModalBottomSheet(
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.8),
context: context,
isScrollControlled: true,
@@ -1121,12 +1188,8 @@ class _VideoPlayerState extends State {
String subtitleEnd = getTimestampFromDuration(subtitle.endTime);
String subtitleDuration = "$subtitleStart - $subtitleEnd";
- int endSubtitleIndex;
-
return ListTile(
- selected: (endSubtitleIndex == null)
- ? i == index
- : i <= index && i >= endSubtitleIndex,
+ selected: i == index,
selectedTileColor: Colors.red.withOpacity(0.15),
dense: true,
title: Column(
@@ -1166,13 +1229,13 @@ class _VideoPlayerState extends State {
onLongPress: () {
if (i <= index) {
List selectedSubtitles = [];
- for (int subIndex = i; subIndex < index; subIndex++) {
+ for (int subIndex = i; subIndex <= index; subIndex++) {
selectedSubtitles.add(subtitles[subIndex]);
}
String selectedText = "";
String removeLastNewline(String n) =>
- n = n.substring(0, n.length - 2);
+ n = n.substring(0, n.length - 1);
selectedSubtitles.forEach(
(subtitle) => selectedText += subtitle.text + "\n");
selectedText = removeLastNewline(selectedText);
@@ -1186,15 +1249,36 @@ class _VideoPlayerState extends State {
endTime: selectedEndTime,
);
- exportToAnki(
- context,
- chewie,
- controller,
- chewie.clipboard,
- selectedSubtitle,
- currentDictionaryEntry,
- false,
+ chewie.clipboard.value = "&<&>export&<&>";
+
+ exportLongCallback(selectedSubtitle);
+ Navigator.pop(context);
+ } else if (i > index) {
+ List selectedSubtitles = [];
+ for (int subIndex = index; subIndex <= i; subIndex++) {
+ selectedSubtitles.add(subtitles[subIndex]);
+ }
+
+ String selectedText = "";
+ String removeLastNewline(String n) =>
+ n = n.substring(0, n.length - 1);
+ selectedSubtitles.forEach(
+ (subtitle) => selectedText += subtitle.text + "\n");
+ selectedText = removeLastNewline(selectedText);
+
+ Duration selectedStartTime = selectedSubtitles.first.startTime;
+ Duration selectedEndTime = selectedSubtitles.last.endTime;
+
+ Subtitle selectedSubtitle = Subtitle(
+ text: selectedText,
+ startTime: selectedStartTime,
+ endTime: selectedEndTime,
);
+
+ chewie.clipboard.value = "&<&>export&<&>";
+
+ exportLongCallback(selectedSubtitle);
+ Navigator.pop(context);
}
});
},
diff --git a/lib/preferences.dart b/lib/preferences.dart
index 0d3d4dc00..61828124d 100644
--- a/lib/preferences.dart
+++ b/lib/preferences.dart
@@ -142,6 +142,14 @@ bool getSelectMode() {
return gSharedPrefs.getBool("selectMode") ?? false;
}
+Future toggleFocusMode() async {
+ await gSharedPrefs.setBool("focusMode", !getFocusMode());
+}
+
+bool getFocusMode() {
+ return gSharedPrefs.getBool("focusMode") ?? false;
+}
+
bool getResumeAvailable() {
String lastPlayedPath = getLastPlayedPath();
return lastPlayedPath != "-1";
diff --git a/lib/youtube.dart b/lib/youtube.dart
index 7859f4059..e49e563a0 100644
--- a/lib/youtube.dart
+++ b/lib/youtube.dart
@@ -149,8 +149,12 @@ Future getPlayerYouTubeInfo(String webURL) async {
return aHeight.compareTo(bHeight);
});
+ for (var stream in streamManifest.audioOnly.sortByBitrate()) {
+ print(stream.audioCodec);
+ }
AudioStreamInfo streamAudioInfo =
streamManifest.audioOnly.sortByBitrate().last;
+
String audioURL = streamAudioInfo.url.toString();
String audioMetadata =
"[${streamAudioInfo.container.name}] - [${streamAudioInfo.bitrate.kiloBitsPerSecond.floor()} Kbps]";
diff --git a/pubspec.lock b/pubspec.lock
index 6a32334c5..adeef162f 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -22,6 +22,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0"
+ audio_service:
+ dependency: "direct main"
+ description:
+ name: audio_service
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.17.0"
+ audio_session:
+ dependency: transitive
+ description:
+ name: audio_session
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.0"
audioplayers:
dependency: "direct main"
description:
@@ -195,6 +209,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.2"
+ flutter_cache_manager:
+ dependency: transitive
+ description:
+ name: flutter_cache_manager
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.1"
flutter_ffmpeg:
dependency: "direct main"
description:
@@ -209,6 +230,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.3.2"
+ flutter_isolate:
+ dependency: transitive
+ description:
+ name: flutter_isolate
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
@@ -470,6 +498,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.3"
+ rxdart:
+ dependency: transitive
+ description:
+ name: rxdart
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.26.0"
scrollable_positioned_list:
dependency: "direct main"
description:
@@ -531,6 +566,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
+ sqflite:
+ dependency: transitive
+ description:
+ name: sqflite
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0+3"
+ sqflite_common:
+ dependency: transitive
+ description:
+ name: sqflite_common
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0+2"
string_scanner:
dependency: transitive
description:
@@ -545,6 +594,13 @@ packages:
relative: true
source: path
version: "1.0.4"
+ synchronized:
+ dependency: transitive
+ description:
+ name: synchronized
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.0"
term_glyph:
dependency: transitive
description:
@@ -714,4 +770,4 @@ packages:
version: "8.0.0"
sdks:
dart: ">=2.12.0 <3.0.0"
- flutter: ">=1.22.2"
+ flutter: ">=1.24.0-10"
diff --git a/pubspec.yaml b/pubspec.yaml
index 395fb9efc..92c5ac14c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,7 +2,7 @@ name: jidoujisho
description: A mobile video player tailored for Japanese language learners.
publish_to: none
-version: 0.13.0+5
+version: 0.13.1+6
environment:
sdk: ">=2.7.0 <3.0.0"
@@ -12,6 +12,7 @@ dependencies:
sdk: flutter
audioplayers:
+ audio_service: ^0.17.0
async:
chewie:
path: ./chewie