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"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 28
|
||||
versionCode 8
|
||||
versionCode 9
|
||||
versionName flutterVersionName
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
|
|
@ -224,7 +224,6 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||
backgroundAudioPosition / 1000, seekSliderValue);
|
||||
await dbHelper.saveHistory(history);
|
||||
AudioService.addQueueItemAt(episodeNew.toMediaItem(), 0);
|
||||
//if (startPosition > 0) AudioService.seekTo(startPosition);
|
||||
_queue.playlist
|
||||
.removeWhere((item) => item.enclosureUrl == episode.enclosureUrl);
|
||||
_queue.playlist.insert(0, episodeNew);
|
||||
|
@ -294,9 +293,10 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||
print(event.length);
|
||||
if (event.length == _queue.playlist.length - 1 &&
|
||||
_audioState == BasicPlaybackState.skippingToNext) {
|
||||
if (event.length == 0 || _stopOnComplete == true) {
|
||||
if (event.length == 0 || _stopOnComplete) {
|
||||
_queue.delFromPlaylist(_episode);
|
||||
_lastPostion = 0;
|
||||
notifyListeners();
|
||||
positionStorage.saveInt(_lastPostion);
|
||||
final PlayHistory history = PlayHistory(
|
||||
_episode.title,
|
||||
|
@ -305,6 +305,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||
seekSliderValue);
|
||||
dbHelper.saveHistory(history);
|
||||
} else if (event.first.id != _episode.mediaId) {
|
||||
_lastPostion = 0;
|
||||
notifyListeners();
|
||||
positionStorage.saveInt(_lastPostion);
|
||||
_queue.delFromPlaylist(_episode);
|
||||
final PlayHistory history = PlayHistory(
|
||||
_episode.title,
|
||||
|
@ -359,7 +362,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
|
|||
} else
|
||||
_seekSliderValue = 0;
|
||||
|
||||
if (_backgroundAudioPosition > 0) {
|
||||
if (_backgroundAudioPosition > 0 &&
|
||||
_backgroundAudioPosition < _backgroundAudioDuration) {
|
||||
_lastPostion = _backgroundAudioPosition;
|
||||
positionStorage.saveInt(_lastPostion);
|
||||
}
|
||||
|
@ -592,7 +596,6 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
Future<void> onStart() async {
|
||||
_stopAtEnd = false;
|
||||
|
||||
print(cacheMax);
|
||||
var playerStateSubscription = _audioPlayer.playbackStateStream
|
||||
.where((state) => state == AudioPlaybackState.completed)
|
||||
.listen((state) {
|
||||
|
@ -647,6 +650,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
await AudioServiceBackground.setQueue(_queue);
|
||||
// }
|
||||
if (_queue.length == 0 || _stopAtEnd) {
|
||||
// await Future.delayed(Duration(milliseconds: 300));
|
||||
_skipState = null;
|
||||
onStop();
|
||||
} else {
|
||||
|
@ -742,8 +746,9 @@ class AudioPlayerTask extends BackgroundAudioTask {
|
|||
@override
|
||||
void onStop() async {
|
||||
await _audioPlayer.stop();
|
||||
await _audioPlayer.dispose();
|
||||
_setState(state: BasicPlaybackState.stopped);
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
_completer?.complete();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,11 @@ class SubscribeItem {
|
|||
String title;
|
||||
SubscribeState subscribeState;
|
||||
String id;
|
||||
String imgUrl;
|
||||
SubscribeItem(this.url, this.title,
|
||||
{this.subscribeState = SubscribeState.none, this.id = ''});
|
||||
{this.subscribeState = SubscribeState.none,
|
||||
this.id = '',
|
||||
this.imgUrl = ''});
|
||||
}
|
||||
|
||||
class SubscribeWorker extends ChangeNotifier {
|
||||
|
@ -56,7 +59,7 @@ class SubscribeWorker extends ChangeNotifier {
|
|||
receivePort.distinct().listen((message) {
|
||||
if (message is SendPort) {
|
||||
subSendPort = message;
|
||||
subSendPort.send([_subscribeItem.url, _subscribeItem.title]);
|
||||
subSendPort.send([_subscribeItem.url, _subscribeItem.title, _subscribeItem.imgUrl]);
|
||||
} else if (message is List) {
|
||||
_setCurrentSubscribeItem(SubscribeItem(message[1], message[0],
|
||||
subscribeState: SubscribeState.values[message[2]],
|
||||
|
@ -76,7 +79,7 @@ class SubscribeWorker extends ChangeNotifier {
|
|||
_created = true;
|
||||
listen();
|
||||
} else
|
||||
subSendPort.send([_subscribeItem.url, _subscribeItem.title]);
|
||||
subSendPort.send([_subscribeItem.url, _subscribeItem.title, _subscribeItem.imgUrl]);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
|
@ -109,8 +112,9 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
|||
receiveTimeout: 20000,
|
||||
);
|
||||
print(rss);
|
||||
Response response = await Dio(options).get(rss);
|
||||
if (response.statusCode == 200) {
|
||||
try {
|
||||
Response response = await Dio(options).get(rss);
|
||||
|
||||
var p = RssFeed.parse(response.data);
|
||||
|
||||
var dir = await getApplicationDocumentsDirectory();
|
||||
|
@ -122,11 +126,41 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
|||
bool checkUrl = await dbHelper.checkPodcast(realUrl);
|
||||
|
||||
if (checkUrl) {
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
p.itunes.image.href,
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
img.Image image = img.decodeImage(imageResponse.data);
|
||||
img.Image thumbnail = img.copyResize(image, width: 300);
|
||||
img.Image thumbnail;
|
||||
try {
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
p.itunes.image.href,
|
||||
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>>(
|
||||
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();
|
||||
File("${dir.path}/$uuid.png")
|
||||
..writeAsBytesSync(img.encodePng(thumbnail));
|
||||
|
@ -173,7 +207,8 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
|||
} else
|
||||
sendPort.send("done");
|
||||
}
|
||||
} else {
|
||||
} 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]);
|
||||
|
@ -187,7 +222,7 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
|||
|
||||
subReceivePort.distinct().listen((message) {
|
||||
if (message is List<String>) {
|
||||
items.add(SubscribeItem(message[0], message[1]));
|
||||
items.add(SubscribeItem(message[0], message[1], imgUrl: message[2]));
|
||||
if (!_running) {
|
||||
_subscribe(items.first);
|
||||
_running = true;
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:tsacdop/util/custompaint.dart';
|
|||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
const String version = '0.2.1';
|
||||
const String version = '0.2.2';
|
||||
|
||||
class AboutApp extends StatelessWidget {
|
||||
_launchUrl(String url) async {
|
||||
|
|
|
@ -259,7 +259,7 @@ class _SearchResultState extends State<SearchResult>
|
|||
var subscribeWorker = Provider.of<SubscribeWorker>(context, listen: false);
|
||||
|
||||
savePodcast(OnlinePodcast podcast) async {
|
||||
SubscribeItem item = SubscribeItem(podcast.rss, podcast.title);
|
||||
SubscribeItem item = SubscribeItem(podcast.rss, podcast.title, imgUrl: podcast.image);
|
||||
subscribeWorker.setSubscribeItem(item);
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,6 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
|||
children: <Widget>[
|
||||
Column(
|
||||
children: <Widget>[
|
||||
Import(),
|
||||
Expanded(
|
||||
child: NestedScrollView(
|
||||
innerScrollPositionKeyBuilder: () {
|
||||
|
@ -101,34 +100,40 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
|||
(BuildContext context, bool innerBoxScrolled) {
|
||||
return <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 50.0,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
tooltip: 'Add',
|
||||
icon: const Icon(
|
||||
Icons.add_circle_outline),
|
||||
onPressed: () async {
|
||||
await showSearch<int>(
|
||||
context: context,
|
||||
delegate: _delegate,
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 50.0,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
tooltip: 'Add',
|
||||
icon: const Icon(
|
||||
Icons.add_circle_outline),
|
||||
onPressed: () async {
|
||||
await showSearch<int>(
|
||||
context: context,
|
||||
delegate: _delegate,
|
||||
);
|
||||
},
|
||||
),
|
||||
Image(
|
||||
image: Theme.of(context)
|
||||
.brightness ==
|
||||
Brightness.light
|
||||
? AssetImage('assets/text.png')
|
||||
: AssetImage(
|
||||
'assets/text_light.png'),
|
||||
height: 30,
|
||||
),
|
||||
PopupMenu(),
|
||||
],
|
||||
),
|
||||
Image(
|
||||
image: Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? AssetImage('assets/text.png')
|
||||
: AssetImage(
|
||||
'assets/text_light.png'),
|
||||
height: 30,
|
||||
),
|
||||
PopupMenu(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Import(),
|
||||
],
|
||||
),
|
||||
),
|
||||
SliverList(
|
||||
|
|
|
@ -16,7 +16,6 @@ final SettingState themeSetting = SettingState();
|
|||
Future main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await themeSetting.initData();
|
||||
|
||||
runApp(
|
||||
MultiProvider(
|
||||
providers: [
|
||||
|
|
|
@ -69,4 +69,5 @@ List<Libries> plugins = [
|
|||
Libries('connectivity', bsd, 'https://pub.dev/packages/connectivity'),
|
||||
Libries('Rxdart', apacheLicense, 'https://pub.dev/packages/rxdart'),
|
||||
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:line_icons/line_icons.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:auto_animated/auto_animated.dart';
|
||||
import 'open_container.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(
|
||||
padding: const EdgeInsets.only(
|
||||
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(
|
||||
childAspectRatio: layout == Layout.three ? 1 : 1.5,
|
||||
crossAxisCount: layout == Layout.three ? 3 : 2,
|
||||
mainAxisSpacing: 6.0,
|
||||
crossAxisSpacing: 6.0,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||
? episodes[index].primaryColor.colorizedark()
|
||||
: episodes[index].primaryColor.colorizeLight();
|
||||
return Selector<AudioPlayerNotifier,
|
||||
itemBuilder: (context, index, animation) {
|
||||
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||
? episodes[index].primaryColor.colorizedark()
|
||||
: episodes[index].primaryColor.colorizeLight();
|
||||
return FadeTransition(
|
||||
opacity: Tween<double>(
|
||||
begin: 0,
|
||||
end: 1,
|
||||
).animate(animation),
|
||||
child: Selector<AudioPlayerNotifier,
|
||||
Tuple2<EpisodeBrief, List<String>>>(
|
||||
selector: (_, audio) => Tuple2(audio?.episode,
|
||||
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
|
||||
rxdart: ^0.24.0
|
||||
flutter_isolate: ^1.0.0+11
|
||||
auto_animated: ^2.0.2
|
||||
|
||||
|
||||
flutter:
|
||||
|
|
Loading…
Reference in New Issue