Add animation for episodegrid
Fix bug when rss feed do not have image Fix bug when use stop at end of episode
This commit is contained in:
parent
69d2568513
commit
006598cb74
|
@ -47,7 +47,7 @@ android {
|
||||||
applicationId "com.stonegate.tsacdop"
|
applicationId "com.stonegate.tsacdop"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 8
|
versionCode 9
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,6 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||||
backgroundAudioPosition / 1000, seekSliderValue);
|
backgroundAudioPosition / 1000, seekSliderValue);
|
||||||
await dbHelper.saveHistory(history);
|
await dbHelper.saveHistory(history);
|
||||||
AudioService.addQueueItemAt(episodeNew.toMediaItem(), 0);
|
AudioService.addQueueItemAt(episodeNew.toMediaItem(), 0);
|
||||||
//if (startPosition > 0) AudioService.seekTo(startPosition);
|
|
||||||
_queue.playlist
|
_queue.playlist
|
||||||
.removeWhere((item) => item.enclosureUrl == episode.enclosureUrl);
|
.removeWhere((item) => item.enclosureUrl == episode.enclosureUrl);
|
||||||
_queue.playlist.insert(0, episodeNew);
|
_queue.playlist.insert(0, episodeNew);
|
||||||
|
@ -294,9 +293,10 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||||
print(event.length);
|
print(event.length);
|
||||||
if (event.length == _queue.playlist.length - 1 &&
|
if (event.length == _queue.playlist.length - 1 &&
|
||||||
_audioState == BasicPlaybackState.skippingToNext) {
|
_audioState == BasicPlaybackState.skippingToNext) {
|
||||||
if (event.length == 0 || _stopOnComplete == true) {
|
if (event.length == 0 || _stopOnComplete) {
|
||||||
_queue.delFromPlaylist(_episode);
|
_queue.delFromPlaylist(_episode);
|
||||||
_lastPostion = 0;
|
_lastPostion = 0;
|
||||||
|
notifyListeners();
|
||||||
positionStorage.saveInt(_lastPostion);
|
positionStorage.saveInt(_lastPostion);
|
||||||
final PlayHistory history = PlayHistory(
|
final PlayHistory history = PlayHistory(
|
||||||
_episode.title,
|
_episode.title,
|
||||||
|
@ -305,6 +305,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||||
seekSliderValue);
|
seekSliderValue);
|
||||||
dbHelper.saveHistory(history);
|
dbHelper.saveHistory(history);
|
||||||
} else if (event.first.id != _episode.mediaId) {
|
} else if (event.first.id != _episode.mediaId) {
|
||||||
|
_lastPostion = 0;
|
||||||
|
notifyListeners();
|
||||||
|
positionStorage.saveInt(_lastPostion);
|
||||||
_queue.delFromPlaylist(_episode);
|
_queue.delFromPlaylist(_episode);
|
||||||
final PlayHistory history = PlayHistory(
|
final PlayHistory history = PlayHistory(
|
||||||
_episode.title,
|
_episode.title,
|
||||||
|
@ -359,7 +362,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
||||||
} else
|
} else
|
||||||
_seekSliderValue = 0;
|
_seekSliderValue = 0;
|
||||||
|
|
||||||
if (_backgroundAudioPosition > 0) {
|
if (_backgroundAudioPosition > 0 &&
|
||||||
|
_backgroundAudioPosition < _backgroundAudioDuration) {
|
||||||
_lastPostion = _backgroundAudioPosition;
|
_lastPostion = _backgroundAudioPosition;
|
||||||
positionStorage.saveInt(_lastPostion);
|
positionStorage.saveInt(_lastPostion);
|
||||||
}
|
}
|
||||||
|
@ -592,7 +596,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
Future<void> onStart() async {
|
Future<void> onStart() async {
|
||||||
_stopAtEnd = false;
|
_stopAtEnd = false;
|
||||||
|
|
||||||
print(cacheMax);
|
|
||||||
var playerStateSubscription = _audioPlayer.playbackStateStream
|
var playerStateSubscription = _audioPlayer.playbackStateStream
|
||||||
.where((state) => state == AudioPlaybackState.completed)
|
.where((state) => state == AudioPlaybackState.completed)
|
||||||
.listen((state) {
|
.listen((state) {
|
||||||
|
@ -647,6 +650,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
await AudioServiceBackground.setQueue(_queue);
|
await AudioServiceBackground.setQueue(_queue);
|
||||||
// }
|
// }
|
||||||
if (_queue.length == 0 || _stopAtEnd) {
|
if (_queue.length == 0 || _stopAtEnd) {
|
||||||
|
// await Future.delayed(Duration(milliseconds: 300));
|
||||||
_skipState = null;
|
_skipState = null;
|
||||||
onStop();
|
onStop();
|
||||||
} else {
|
} else {
|
||||||
|
@ -742,8 +746,9 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
||||||
@override
|
@override
|
||||||
void onStop() async {
|
void onStop() async {
|
||||||
await _audioPlayer.stop();
|
await _audioPlayer.stop();
|
||||||
|
await _audioPlayer.dispose();
|
||||||
_setState(state: BasicPlaybackState.stopped);
|
_setState(state: BasicPlaybackState.stopped);
|
||||||
await Future.delayed(Duration(milliseconds: 500));
|
await Future.delayed(Duration(milliseconds: 300));
|
||||||
_completer?.complete();
|
_completer?.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,11 @@ class SubscribeItem {
|
||||||
String title;
|
String title;
|
||||||
SubscribeState subscribeState;
|
SubscribeState subscribeState;
|
||||||
String id;
|
String id;
|
||||||
|
String imgUrl;
|
||||||
SubscribeItem(this.url, this.title,
|
SubscribeItem(this.url, this.title,
|
||||||
{this.subscribeState = SubscribeState.none, this.id = ''});
|
{this.subscribeState = SubscribeState.none,
|
||||||
|
this.id = '',
|
||||||
|
this.imgUrl = ''});
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubscribeWorker extends ChangeNotifier {
|
class SubscribeWorker extends ChangeNotifier {
|
||||||
|
@ -56,7 +59,7 @@ class SubscribeWorker extends ChangeNotifier {
|
||||||
receivePort.distinct().listen((message) {
|
receivePort.distinct().listen((message) {
|
||||||
if (message is SendPort) {
|
if (message is SendPort) {
|
||||||
subSendPort = message;
|
subSendPort = message;
|
||||||
subSendPort.send([_subscribeItem.url, _subscribeItem.title]);
|
subSendPort.send([_subscribeItem.url, _subscribeItem.title, _subscribeItem.imgUrl]);
|
||||||
} else if (message is List) {
|
} else if (message is List) {
|
||||||
_setCurrentSubscribeItem(SubscribeItem(message[1], message[0],
|
_setCurrentSubscribeItem(SubscribeItem(message[1], message[0],
|
||||||
subscribeState: SubscribeState.values[message[2]],
|
subscribeState: SubscribeState.values[message[2]],
|
||||||
|
@ -76,7 +79,7 @@ class SubscribeWorker extends ChangeNotifier {
|
||||||
_created = true;
|
_created = true;
|
||||||
listen();
|
listen();
|
||||||
} else
|
} else
|
||||||
subSendPort.send([_subscribeItem.url, _subscribeItem.title]);
|
subSendPort.send([_subscribeItem.url, _subscribeItem.title, _subscribeItem.imgUrl]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
@ -109,8 +112,9 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||||
receiveTimeout: 20000,
|
receiveTimeout: 20000,
|
||||||
);
|
);
|
||||||
print(rss);
|
print(rss);
|
||||||
|
try {
|
||||||
Response response = await Dio(options).get(rss);
|
Response response = await Dio(options).get(rss);
|
||||||
if (response.statusCode == 200) {
|
|
||||||
var p = RssFeed.parse(response.data);
|
var p = RssFeed.parse(response.data);
|
||||||
|
|
||||||
var dir = await getApplicationDocumentsDirectory();
|
var dir = await getApplicationDocumentsDirectory();
|
||||||
|
@ -122,11 +126,41 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||||
bool checkUrl = await dbHelper.checkPodcast(realUrl);
|
bool checkUrl = await dbHelper.checkPodcast(realUrl);
|
||||||
|
|
||||||
if (checkUrl) {
|
if (checkUrl) {
|
||||||
|
img.Image thumbnail;
|
||||||
|
try {
|
||||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||||
p.itunes.image.href,
|
p.itunes.image.href,
|
||||||
options: Options(responseType: ResponseType.bytes));
|
options: Options(responseType: ResponseType.bytes));
|
||||||
img.Image image = img.decodeImage(imageResponse.data);
|
img.Image image = img.decodeImage(imageResponse.data);
|
||||||
img.Image thumbnail = img.copyResize(image, width: 300);
|
thumbnail = img.copyResize(image, width: 300);
|
||||||
|
} on DioError catch (e) {
|
||||||
|
print(e);
|
||||||
|
try {
|
||||||
|
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||||
|
item.imgUrl,
|
||||||
|
options: Options(responseType: ResponseType.bytes));
|
||||||
|
img.Image image = img.decodeImage(imageResponse.data);
|
||||||
|
thumbnail = img.copyResize(image, width: 300);
|
||||||
|
} on DioError catch (e) {
|
||||||
|
print(e);
|
||||||
|
try {
|
||||||
|
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||||
|
"https://ui-avatars.com/api/?size=300&background=4D91BE&color=fff&name=${item.title}&length=2&bold=true",
|
||||||
|
options: Options(responseType: ResponseType.bytes));
|
||||||
|
thumbnail = img.decodeImage(imageResponse.data);
|
||||||
|
} on DioError catch (e) {
|
||||||
|
print(e);
|
||||||
|
sendPort.send([item.title, item.url, 6]);
|
||||||
|
await Future.delayed(Duration(seconds: 2));
|
||||||
|
sendPort.send([item.title, item.url, 4]);
|
||||||
|
items.removeWhere((element) => element.url == item.url);
|
||||||
|
if (items.length > 0) {
|
||||||
|
await _subscribe(items.first);
|
||||||
|
} else
|
||||||
|
sendPort.send("done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
String uuid = Uuid().v4();
|
String uuid = Uuid().v4();
|
||||||
File("${dir.path}/$uuid.png")
|
File("${dir.path}/$uuid.png")
|
||||||
..writeAsBytesSync(img.encodePng(thumbnail));
|
..writeAsBytesSync(img.encodePng(thumbnail));
|
||||||
|
@ -173,7 +207,8 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||||
} else
|
} else
|
||||||
sendPort.send("done");
|
sendPort.send("done");
|
||||||
}
|
}
|
||||||
} else {
|
} on DioError catch (e) {
|
||||||
|
print(e);
|
||||||
sendPort.send([item.title, item.url, 6]);
|
sendPort.send([item.title, item.url, 6]);
|
||||||
await Future.delayed(Duration(seconds: 2));
|
await Future.delayed(Duration(seconds: 2));
|
||||||
sendPort.send([item.title, item.url, 4]);
|
sendPort.send([item.title, item.url, 4]);
|
||||||
|
@ -187,7 +222,7 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||||
|
|
||||||
subReceivePort.distinct().listen((message) {
|
subReceivePort.distinct().listen((message) {
|
||||||
if (message is List<String>) {
|
if (message is List<String>) {
|
||||||
items.add(SubscribeItem(message[0], message[1]));
|
items.add(SubscribeItem(message[0], message[1], imgUrl: message[2]));
|
||||||
if (!_running) {
|
if (!_running) {
|
||||||
_subscribe(items.first);
|
_subscribe(items.first);
|
||||||
_running = true;
|
_running = true;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:tsacdop/util/custompaint.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
|
||||||
const String version = '0.2.1';
|
const String version = '0.2.2';
|
||||||
|
|
||||||
class AboutApp extends StatelessWidget {
|
class AboutApp extends StatelessWidget {
|
||||||
_launchUrl(String url) async {
|
_launchUrl(String url) async {
|
||||||
|
|
|
@ -259,7 +259,7 @@ class _SearchResultState extends State<SearchResult>
|
||||||
var subscribeWorker = Provider.of<SubscribeWorker>(context, listen: false);
|
var subscribeWorker = Provider.of<SubscribeWorker>(context, listen: false);
|
||||||
|
|
||||||
savePodcast(OnlinePodcast podcast) async {
|
savePodcast(OnlinePodcast podcast) async {
|
||||||
SubscribeItem item = SubscribeItem(podcast.rss, podcast.title);
|
SubscribeItem item = SubscribeItem(podcast.rss, podcast.title, imgUrl: podcast.image);
|
||||||
subscribeWorker.setSubscribeItem(item);
|
subscribeWorker.setSubscribeItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,6 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Column(
|
Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Import(),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: NestedScrollView(
|
child: NestedScrollView(
|
||||||
innerScrollPositionKeyBuilder: () {
|
innerScrollPositionKeyBuilder: () {
|
||||||
|
@ -101,7 +100,9 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
(BuildContext context, bool innerBoxScrolled) {
|
(BuildContext context, bool innerBoxScrolled) {
|
||||||
return <Widget>[
|
return <Widget>[
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: SizedBox(
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
height: 50.0,
|
height: 50.0,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
|
@ -119,7 +120,8 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Image(
|
Image(
|
||||||
image: Theme.of(context).brightness ==
|
image: Theme.of(context)
|
||||||
|
.brightness ==
|
||||||
Brightness.light
|
Brightness.light
|
||||||
? AssetImage('assets/text.png')
|
? AssetImage('assets/text.png')
|
||||||
: AssetImage(
|
: AssetImage(
|
||||||
|
@ -130,6 +132,9 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Import(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
|
|
@ -16,7 +16,6 @@ final SettingState themeSetting = SettingState();
|
||||||
Future main() async {
|
Future main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await themeSetting.initData();
|
await themeSetting.initData();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
|
|
@ -69,4 +69,5 @@ List<Libries> plugins = [
|
||||||
Libries('connectivity', bsd, 'https://pub.dev/packages/connectivity'),
|
Libries('connectivity', bsd, 'https://pub.dev/packages/connectivity'),
|
||||||
Libries('Rxdart', apacheLicense, 'https://pub.dev/packages/rxdart'),
|
Libries('Rxdart', apacheLicense, 'https://pub.dev/packages/rxdart'),
|
||||||
Libries('flutter_isolate', mit, 'https://pub.dev/packages/flutter_isolate'),
|
Libries('flutter_isolate', mit, 'https://pub.dev/packages/flutter_isolate'),
|
||||||
|
Libries('auto_animated', mit, 'https://pub.dev/packages/auto_animated'),
|
||||||
];
|
];
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:auto_animated/auto_animated.dart';
|
||||||
import 'open_container.dart';
|
import 'open_container.dart';
|
||||||
|
|
||||||
import 'package:tsacdop/class/audiostate.dart';
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
|
@ -122,22 +123,35 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final options = LiveOptions(
|
||||||
|
delay: Duration(milliseconds: 0),
|
||||||
|
showItemInterval: Duration(milliseconds: 100),
|
||||||
|
showItemDuration: Duration(milliseconds: 100),
|
||||||
|
);
|
||||||
|
final scrollController = ScrollController();
|
||||||
return SliverPadding(
|
return SliverPadding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 10.0, bottom: 5.0, left: 15.0, right: 15.0),
|
top: 10.0, bottom: 5.0, left: 15.0, right: 15.0),
|
||||||
sliver: SliverGrid(
|
sliver: LiveSliverGrid.options(
|
||||||
|
controller: scrollController,
|
||||||
|
options: options,
|
||||||
|
itemCount: episodes.length,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
childAspectRatio: layout == Layout.three ? 1 : 1.5,
|
childAspectRatio: layout == Layout.three ? 1 : 1.5,
|
||||||
crossAxisCount: layout == Layout.three ? 3 : 2,
|
crossAxisCount: layout == Layout.three ? 3 : 2,
|
||||||
mainAxisSpacing: 6.0,
|
mainAxisSpacing: 6.0,
|
||||||
crossAxisSpacing: 6.0,
|
crossAxisSpacing: 6.0,
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
itemBuilder: (context, index, animation) {
|
||||||
(BuildContext context, int index) {
|
|
||||||
Color _c = (Theme.of(context).brightness == Brightness.light)
|
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||||
? episodes[index].primaryColor.colorizedark()
|
? episodes[index].primaryColor.colorizedark()
|
||||||
: episodes[index].primaryColor.colorizeLight();
|
: episodes[index].primaryColor.colorizeLight();
|
||||||
return Selector<AudioPlayerNotifier,
|
return FadeTransition(
|
||||||
|
opacity: Tween<double>(
|
||||||
|
begin: 0,
|
||||||
|
end: 1,
|
||||||
|
).animate(animation),
|
||||||
|
child: Selector<AudioPlayerNotifier,
|
||||||
Tuple2<EpisodeBrief, List<String>>>(
|
Tuple2<EpisodeBrief, List<String>>>(
|
||||||
selector: (_, audio) => Tuple2(audio?.episode,
|
selector: (_, audio) => Tuple2(audio?.episode,
|
||||||
audio.queue.playlist.map((e) => e.enclosureUrl).toList()),
|
audio.queue.playlist.map((e) => e.enclosureUrl).toList()),
|
||||||
|
@ -441,10 +455,9 @@ class EpisodeGrid extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: episodes.length,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ dev_dependencies:
|
||||||
flare_flutter: ^2.0.3
|
flare_flutter: ^2.0.3
|
||||||
rxdart: ^0.24.0
|
rxdart: ^0.24.0
|
||||||
flutter_isolate: ^1.0.0+11
|
flutter_isolate: ^1.0.0+11
|
||||||
|
auto_animated: ^2.0.2
|
||||||
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
Loading…
Reference in New Issue