🐛 Bugs fixed

This commit is contained in:
stonegate 2020-06-06 02:33:47 +08:00
parent 137fe58183
commit ab6910d9c6
13 changed files with 79 additions and 54 deletions

View File

@ -1,4 +1,3 @@
<p align="center">
<img src="https://raw.githubusercontent.com/stonega/tsacdop/master/android/app/src/main/res/mipmap-xhdpi/ic_notification.png" art = "Logo"/>
</br>
@ -6,11 +5,13 @@
</p>
![CircleCI](https://img.shields.io/circleci/build/github/stonega/tsacdop?token=efe1331861e017144f2abb363acd95197e436dad)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/stonega/tsacdop)
[![GooglePlay](https://img.shields.io/badge/Google-PlayStore-%2323CCC6)](https://play.google.com/store/apps/details?id=com.stonegate.tsacdop)
## About
Enjoy podcasts with Tsacdop.
Tsacdop is a podcast player developed with flutter, a clean, simply beautiful and friendly app, only support Android right now.
@ -20,6 +21,7 @@ Credit to flutter team and all involved plugins, especially [webfeed](https://g
The podcasts search engine is powered by [ListenNotes](https://listennotes.com).
## Features
* Podcasts group management
* Playlist support
* Sleep timer / Speed setting
@ -33,9 +35,10 @@ The podcasts search engine is powered by [ListenNotes](https://listennotes.com).
More to come...
## Preview
HomePage | Group | Podcast | Episode |DarkMode
-------|--------|--------|------| ----
<img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585893838840.png" art = "HomePage"/>|<img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585894051734.png" art = "Groups"/>|<img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585893877702.png" art = "Podcast"/>|<img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585896237809.png" art = "Episode"/>|<img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585893920721.png" art = "DarkMode"/>|
| HomePage | Group | Podcast | Episode | DarkMode |
|------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| <img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585893838840.png" art = "HomePage"/> | <img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585894051734.png" art = "Groups"/> | <img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585893877702.png" art = "Podcast"/> | <img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585896237809.png" art = "Episode"/> | <img src="https://raw.githubusercontent.com/stonega/tsacdop/master/preview/1585893920721.png" art = "DarkMode"/> |
## License
@ -46,11 +49,11 @@ Tsacdop is licensed under the [GPL V3.0](https://github.com/stonega/tsacdop/blob
Tsacdop is using ListenNotes api 1.0 pro to search podcast, which is not free. So I can not expose the api key in the repo.
If you want to build the app, you need to create a new file named .env.dart in lib folder. Add below code in .env.dart.
```
final environment = {"apiKey":"APIKEY", "shareKey":"SHAREKEY"};
``llkk
final environment = {"apiKey":"APIKEY", "shareKey":"SHAREKEY"};
```
You can get own api key on [RapidApi](https://rapidapi.com/listennotes/api/listennotes), basic plan is free to all, and replace "APIKEY" with it.
You can get own api key on [ListenNotes](https://www.listennotes.com/api/), basic plan is free to all, and replace "APIKEY" with it.
If no api key added, the search function in the app won't work. But you can still add podcasts by serach rss link or import ompl file.
Share_key is used for generate clip.
@ -61,9 +64,9 @@ This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
* [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
* [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -48,8 +48,8 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.stonegate.tsacdop"
minSdkVersion 19
targetSdkVersion 28
versionCode 15
targetSdkVersion 29
versionCode 16
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

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.3.2';
const String version = '0.3.3';
class AboutApp extends StatelessWidget {
_launchUrl(String url) async {

View File

@ -440,12 +440,14 @@ class _RecentUpdateState extends State<_RecentUpdate>
String _groupName;
List<String> _group;
Layout _layout;
bool _scroll;
@override
void initState() {
super.initState();
_loadMore = false;
_groupName = 'All';
_group = ['All'];
_scroll = false;
}
@override
@ -479,6 +481,11 @@ class _RecentUpdateState extends State<_RecentUpdate>
)
: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo is ScrollStartNotification &&
mounted &&
!_scroll) {
setState(() => _scroll = true);
}
if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent &&
snapshot.data.length == _top)
@ -705,7 +712,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
EpisodeGrid(
episodes: snapshot.data,
layout: _layout,
initNum: 9,
initNum: _scroll ? 0 : 12,
),
SliverList(
delegate: SliverChildBuilderDelegate(

View File

@ -4,7 +4,6 @@ import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../state/podcast_group.dart';
import '../util/episodegrid.dart';
const String autoPlayKey = 'autoPlay';
const String autoAddKey = 'autoAdd';

View File

@ -23,7 +23,6 @@ import '../util/colorize.dart';
import '../util/context_extension.dart';
import '../util/custompaint.dart';
import '../state/audiostate.dart';
import '../state/podcast_group.dart';
class PodcastDetail extends StatefulWidget {
PodcastDetail({Key key, @required this.podcastLocal, this.hide = false})
@ -41,6 +40,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
List<PodcastHost> _hosts;
int _episodeCount;
Layout _layout;
bool _scroll;
Future _updateRssItem(PodcastLocal podcastLocal) async {
var dbHelper = DBHelper();
@ -196,6 +196,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
_top = 99;
_reverse = false;
_controller = ScrollController();
_scroll = false;
}
@override
@ -257,6 +258,10 @@ class _PodcastDetailState extends State<PodcastDetail> {
_loadMore = false;
});
}
if (_controller.offset > 0 && mounted && !_scroll )
setState(() {
_scroll = true;
});
}),
physics:
const AlwaysScrollableScrollPhysics(),
@ -557,6 +562,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
layout: _layout,
reverse: _reverse,
episodeCount: _episodeCount,
initNum: _scroll ? 0 : 12,
),
SliverList(
delegate: SliverChildBuilderDelegate(

View File

@ -15,7 +15,6 @@ import '../util/pageroute.dart';
import '../util/colorize.dart';
import '../util/duraiton_picker.dart';
import '../util/context_extension.dart';
import '../state/audiostate.dart';
class PodcastGroupList extends StatefulWidget {
final PodcastGroup group;

View File

@ -52,8 +52,7 @@ class ThemeSetting extends StatelessWidget {
.modalBarrierDismissLabel,
barrierColor: Colors.black54,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (BuildContext context,
Animation animaiton,
pageBuilder: (BuildContext context, Animation animaiton,
Animation secondaryAnimation) =>
AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
@ -72,15 +71,14 @@ class ThemeSetting extends StatelessWidget {
),
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10.0))),
borderRadius:
BorderRadius.all(Radius.circular(10.0))),
title: Text('Theme'),
content: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment:
MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
RadioListTile(
title: Text('System default'),
@ -123,8 +121,8 @@ class ThemeSetting extends StatelessWidget {
title: Text(
'Real Dark',
),
subtitle: Text(
'Turn on if you think the night is not dark enough'),
subtitle:
Text('Turn on if you think the night is not dark enough'),
trailing: Selector<SettingState, bool>(
selector: (_, setting) => setting.realDark,
builder: (_, data, __) => Switch(
@ -143,8 +141,7 @@ class ThemeSetting extends StatelessWidget {
.modalBarrierDismissLabel,
barrierColor: Colors.black54,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (BuildContext context,
Animation animaiton,
pageBuilder: (BuildContext context, Animation animaiton,
Animation secondaryAnimation) =>
AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
@ -160,7 +157,7 @@ class ThemeSetting extends StatelessWidget {
titlePadding: EdgeInsets.only(
top: 20,
left: 40,
right: 200,
right: context.width / 3,
bottom: 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(

View File

@ -96,9 +96,11 @@ class Playlist {
}
addToPlayList(EpisodeBrief episodeBrief) async {
_playlist.add(episodeBrief);
await savePlaylist();
dbHelper.removeEpisodeNewMark(episodeBrief.enclosureUrl);
if (!_playlist.contains(episodeBrief)) {
_playlist.add(episodeBrief);
await savePlaylist();
dbHelper.removeEpisodeNewMark(episodeBrief.enclosureUrl);
}
}
addToPlayListAt(EpisodeBrief episodeBrief, int index) async {

View File

@ -34,7 +34,7 @@ class EpisodeBrief {
this.skipSeconds);
String dateToString() {
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate,isUtc: true);
DateTime date = DateTime.fromMillisecondsSinceEpoch(pubDate, isUtc: true);
var diffrence = DateTime.now().toUtc().difference(date);
if (diffrence.inHours < 1) {
return '1 hour ago';
@ -56,9 +56,17 @@ class EpisodeBrief {
title: title,
artist: feedTitle,
album: feedTitle,
// duration: 0,
// duration: 0,
artUri: 'file://$imagePath',
extras: {'skip': skipSeconds});
}
@override
bool operator ==(Object episode) =>
episode is EpisodeBrief &&
episode.title == title &&
episode.enclosureUrl == enclosureUrl;
@override
int get hashCode => enclosureUrl.hashCode;
}

View File

@ -29,6 +29,18 @@ class EpisodeGrid extends StatelessWidget {
final Layout layout;
final bool reverse;
final int initNum;
EpisodeGrid({
Key key,
@required this.episodes,
this.initNum = 12,
this.showDownload = false,
this.showFavorite = false,
this.showNumber = false,
this.episodeCount = 0,
this.layout = Layout.three,
this.reverse,
}) : super(key: key);
Future<int> _isListened(EpisodeBrief episode) async {
DBHelper dbHelper = DBHelper();
return await dbHelper.isListened(episode.enclosureUrl);
@ -44,18 +56,6 @@ class EpisodeGrid extends StatelessWidget {
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
EpisodeGrid({
Key key,
@required this.episodes,
this.initNum = 12,
this.showDownload = false,
this.showFavorite = false,
this.showNumber = false,
this.episodeCount = 0,
this.layout = Layout.three,
this.reverse,
}) : super(key: key);
Widget _title(EpisodeBrief episode) => Container(
alignment:
layout == Layout.one ? Alignment.centerLeft : Alignment.topLeft,
@ -67,6 +67,7 @@ class EpisodeGrid extends StatelessWidget {
layout == Layout.one ? TextOverflow.ellipsis : TextOverflow.fade,
),
);
Widget _circleImage(BuildContext context,
{EpisodeBrief episode, Color color, bool boo}) =>
Container(
@ -79,6 +80,7 @@ class EpisodeGrid extends StatelessWidget {
backgroundImage: FileImage(File("${episode.imagePath}")),
),
);
Widget _listenIndicater(BuildContext context,
{EpisodeBrief episode, int isListened}) =>
Selector<AudioPlayerNotifier, Tuple2<EpisodeBrief, bool>>(
@ -133,6 +135,7 @@ class EpisodeGrid extends StatelessWidget {
: Center(),
)
: Center();
Widget _isNewIndicator(EpisodeBrief episode) => episode.isNew == 1
? Container(
padding: EdgeInsets.symmetric(horizontal: 2),
@ -158,6 +161,7 @@ class EpisodeGrid extends StatelessWidget {
),
)
: Center();
Widget _pubDate(BuildContext context, {EpisodeBrief episode, Color color}) =>
Text(
episode.dateToString(),
@ -166,7 +170,6 @@ class EpisodeGrid extends StatelessWidget {
color: color,
fontStyle: FontStyle.italic),
);
@override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
@ -242,6 +245,7 @@ class EpisodeGrid extends StatelessWidget {
showItemDuration: Duration(milliseconds: 50),
);
final scrollController = ScrollController();
return SliverPadding(
padding: const EdgeInsets.only(
top: 10.0, bottom: 5.0, left: 15.0, right: 15.0),
@ -261,11 +265,12 @@ class EpisodeGrid extends StatelessWidget {
Color _c = (Theme.of(context).brightness == Brightness.light)
? episodes[index].primaryColor.colorizedark()
: episodes[index].primaryColor.colorizeLight();
scrollController.addListener(() {
print(scrollController.offset);
});
return FadeTransition(
opacity: Tween<double>(
begin: index < initNum ? 0 : 1,
end: 1,
).animate(animation),
opacity: Tween<double>(begin: index < initNum ? 0 : 1, end: 1)
.animate(animation),
child: Selector<AudioPlayerNotifier,
Tuple2<EpisodeBrief, List<String>>>(
selector: (_, audio) => Tuple2(audio?.episode,

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'context_extension.dart';
//Slide Transition
class SlideLeftRoute extends PageRouteBuilder {

View File

@ -1,7 +1,7 @@
name: tsacdop
description: An easy-use podacasts player.
version: 0.3.2
version: 0.3.3
environment:
sdk: ">=2.6.0 <3.0.0"
@ -41,9 +41,9 @@ dependencies:
connectivity: ^0.4.8+2
flare_flutter: ^2.0.3
rxdart: ^0.24.0
auto_animated: ^2.1.0
wc_flutter_share: ^0.2.1
video_player: ^0.10.11
auto_animated: ^2.1.0
just_audio:
git:
url: https://github.com/stonega/just_audio.git