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:
stonegate 2020-05-06 20:08:41 +08:00
parent 69d2568513
commit 006598cb74
10 changed files with 119 additions and 60 deletions

View File

@ -47,7 +47,7 @@ android {
applicationId "com.stonegate.tsacdop"
minSdkVersion 19
targetSdkVersion 28
versionCode 8
versionCode 9
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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 {

View File

@ -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);
}

View File

@ -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(

View File

@ -16,7 +16,6 @@ final SettingState themeSetting = SettingState();
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await themeSetting.initData();
runApp(
MultiProvider(
providers: [

View File

@ -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'),
];

View File

@ -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,
),
),
);
},
),
);
}

View File

@ -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: