mirror of
https://github.com/stonega/tsacdop
synced 2025-02-18 04:20:37 +01:00
🐛 Beter support for small screen.
This commit is contained in:
parent
8ec1793843
commit
e9ba82d5db
@ -20,7 +20,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
|
||||
* Subscriptoin group management
|
||||
* Podcasts group management
|
||||
* Playlist support
|
||||
* Sleep timer / Speed setting
|
||||
* OMPL file export and import
|
||||
@ -28,7 +28,7 @@ The podcasts search engine is powered by [ListenNotes](https://listennotes.com).
|
||||
* Listen and subscribe history record
|
||||
* Dark mode / Accent color
|
||||
* Download for offline playing
|
||||
* Share clip on twitter
|
||||
* Share clip(video format) on twitter
|
||||
|
||||
More to come...
|
||||
|
||||
|
@ -49,7 +49,7 @@ android {
|
||||
applicationId "com.stonegate.tsacdop"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 28
|
||||
versionCode 14
|
||||
versionCode 15
|
||||
versionName flutterVersionName
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
@ -580,7 +580,7 @@ class _MenuBarState extends State<MenuBar> {
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Text('Play Now',
|
||||
Text('Play',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).accentColor,
|
||||
fontSize: 15,
|
||||
|
@ -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.1';
|
||||
const String version = '0.3.2';
|
||||
|
||||
class AboutApp extends StatelessWidget {
|
||||
_launchUrl(String url) async {
|
||||
@ -74,105 +74,110 @@ class AboutApp extends StatelessWidget {
|
||||
title: Text('About'),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(20),
|
||||
alignment: Alignment.topLeft,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 200.0,
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Image(
|
||||
image: AssetImage('assets/logo.png'),
|
||||
height: 80,
|
||||
),
|
||||
Text('Version: $version'),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 50),
|
||||
child: Text(
|
||||
'Tsacdop is a podcast player developed in flutter, a clean, simply beautiful and friendly app.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).accentColor, width: 1),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
'Developer',
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).accentColor),
|
||||
),
|
||||
),
|
||||
_listItem(context, 'Twitter', LineIcons.twitter,
|
||||
'https://twitter.com/shimenmen'),
|
||||
_listItem(context, 'GitHub', LineIcons.github_alt,
|
||||
'https://github.com/stonega'),
|
||||
_listItem(context, 'Medium', LineIcons.medium,
|
||||
'https://medium.com/@stonegate'),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Container(
|
||||
height: 50,
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTapDown: (detail) async {
|
||||
OverlayEntry _overlayEntry;
|
||||
_overlayEntry = _createOverlayEntry(detail);
|
||||
Overlay.of(context).insert(_overlayEntry);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
_overlayEntry?.remove();
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 200.0,
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
'assets/text.png',
|
||||
height: 25,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
),
|
||||
Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
),
|
||||
FlutterLogo(
|
||||
size: 18,
|
||||
Image(
|
||||
image: AssetImage('assets/logo.png'),
|
||||
height: 80,
|
||||
),
|
||||
Text('Version: $version'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 50),
|
||||
child: Text(
|
||||
'Tsacdop is a podcast player developed in flutter, a clean, simply beautiful and friendly app.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).accentColor, width: 1),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
'Developer',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).accentColor),
|
||||
),
|
||||
),
|
||||
_listItem(context, 'Twitter', LineIcons.twitter,
|
||||
'https://twitter.com/shimenmen'),
|
||||
_listItem(context, 'GitHub', LineIcons.github_alt,
|
||||
'https://github.com/stonega'),
|
||||
_listItem(context, 'Medium', LineIcons.medium,
|
||||
'https://medium.com/@stonegate'),
|
||||
],
|
||||
),
|
||||
),
|
||||
//Spacer(),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 50),
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTapDown: (detail) async {
|
||||
OverlayEntry _overlayEntry;
|
||||
_overlayEntry = _createOverlayEntry(detail);
|
||||
Overlay.of(context).insert(_overlayEntry);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
_overlayEntry?.remove();
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Image.asset(
|
||||
'assets/text.png',
|
||||
height: 25,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
),
|
||||
Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||
),
|
||||
FlutterLogo(
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
|
@ -18,24 +18,26 @@ import '../.env.dart';
|
||||
|
||||
class MyHomePageDelegate extends SearchDelegate<int> {
|
||||
final String searchFieldLabel;
|
||||
|
||||
MyHomePageDelegate({this.searchFieldLabel})
|
||||
: super(
|
||||
searchFieldLabel: searchFieldLabel,
|
||||
);
|
||||
static Future<List> getList(String searchText) async {
|
||||
String apiKey = environment['apiKey'];
|
||||
String url =
|
||||
"https://listennotes.p.rapidapi.com/api/v1/search?only_in=title%2Cdescription&q=" +
|
||||
"$searchText&type=podcast";
|
||||
Response response = await Dio().get(url,
|
||||
options: Options(headers: {
|
||||
'X-RapidAPI-Key': "$apiKey",
|
||||
'Accept': "application/json"
|
||||
}));
|
||||
Map searchResultMap = jsonDecode(response.toString());
|
||||
var searchResult = SearchPodcast.fromJson(searchResultMap);
|
||||
return searchResult.results;
|
||||
}
|
||||
searchFieldLabel: searchFieldLabel,
|
||||
);
|
||||
|
||||
//static Future<List> getList(String searchText) async {
|
||||
// String apiKey = environment['apiKey'];
|
||||
// String url =
|
||||
// "https://listennotes.p.rapidapi.com/api/v1/search?only_in=title%2Cdescription&q=" +
|
||||
// "$searchText&type=podcast";
|
||||
// Response response = await Dio().get(url,
|
||||
// options: Options(headers: {
|
||||
// 'X-RapidAPI-Key': "$apiKey",
|
||||
// 'Accept': "application/json"
|
||||
// }));
|
||||
// Map searchResultMap = jsonDecode(response.toString());
|
||||
// var searchResult = SearchPodcast.fromJson(searchResultMap);
|
||||
// return searchResult.results;
|
||||
//}
|
||||
|
||||
static Future getRss(String url) async {
|
||||
try {
|
||||
@ -185,27 +187,130 @@ class MyHomePageDelegate extends SearchDelegate<int> {
|
||||
},
|
||||
);
|
||||
else
|
||||
return FutureBuilder(
|
||||
future: getList(query),
|
||||
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
|
||||
if (!snapshot.hasData && query != null)
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 200),
|
||||
alignment: Alignment.topCenter,
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
List content = snapshot.data;
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
itemCount: content.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return SearchResult(
|
||||
onlinePodcast: content[index],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
return SearchList(
|
||||
query: query,
|
||||
);
|
||||
// return FutureBuilder(
|
||||
// future: getList(query),
|
||||
// builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
|
||||
// if (!snapshot.hasData && query != null)
|
||||
// return Container(
|
||||
// padding: EdgeInsets.only(top: 200),
|
||||
// alignment: Alignment.topCenter,
|
||||
// child: CircularProgressIndicator(),
|
||||
// );
|
||||
// List content = snapshot.data;
|
||||
// return ListView.builder(
|
||||
// scrollDirection: Axis.vertical,
|
||||
// itemCount: content.length,
|
||||
// itemBuilder: (BuildContext context, int index) {
|
||||
// return SearchResult(
|
||||
// onlinePodcast: content[index],
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
class SearchList extends StatefulWidget {
|
||||
final String query;
|
||||
SearchList({this.query, Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SearchListState createState() => _SearchListState();
|
||||
}
|
||||
|
||||
class _SearchListState extends State<SearchList> {
|
||||
int _nextOffset;
|
||||
List<OnlinePodcast> _podcastList;
|
||||
int _offset;
|
||||
bool _loading;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nextOffset = 0;
|
||||
_podcastList = [];
|
||||
}
|
||||
|
||||
Future<List> _getList(String searchText, int nextOffset) async {
|
||||
String apiKey = environment['apiKey'];
|
||||
String url = "https://listen-api.listennotes.com/api/v2/search?q=" +
|
||||
searchText +
|
||||
"&sort_by_date=0&type=podcast&offset=$nextOffset";
|
||||
Response response = await Dio().get(url,
|
||||
options: Options(headers: {
|
||||
'X-ListenAPI-Key': "$apiKey",
|
||||
'Accept': "application/json"
|
||||
}));
|
||||
Map searchResultMap = jsonDecode(response.toString());
|
||||
var searchResult = SearchPodcast.fromJson(searchResultMap);
|
||||
_offset = searchResult.nextOffset;
|
||||
_podcastList.addAll(searchResult.results.cast());
|
||||
_loading = false;
|
||||
return _podcastList;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<List>(
|
||||
future: _getList(widget.query, _nextOffset),
|
||||
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
|
||||
if (!snapshot.hasData && widget.query != null)
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 200),
|
||||
alignment: Alignment.topCenter,
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
var content = snapshot.data;
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return SearchResult(
|
||||
onlinePodcast: content[index],
|
||||
);
|
||||
},
|
||||
childCount: content.length,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10.0, bottom: 20.0),
|
||||
child: SizedBox(
|
||||
height: 30,
|
||||
child: OutlineButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(15))),
|
||||
child: _loading
|
||||
? SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
))
|
||||
: Text('Load more'),
|
||||
onPressed: () => _loading
|
||||
? null
|
||||
: setState(() {
|
||||
_loading = true;
|
||||
_nextOffset = _offset;
|
||||
}))),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,6 +397,25 @@ class _SearchResultState extends State<SearchResult>
|
||||
width: 40.0,
|
||||
fit: BoxFit.fitWidth,
|
||||
alignment: Alignment.center,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
height: 40,
|
||||
width: 40,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: context.primaryColorDark),
|
||||
child: SizedBox(
|
||||
width: 20, height: 2, child: LinearProgressIndicator()),
|
||||
);
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) => Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
alignment: Alignment.center,
|
||||
color: context.primaryColorDark,
|
||||
child: Icon(Icons.error)),
|
||||
),
|
||||
),
|
||||
title: Text(widget.onlinePodcast.title),
|
||||
|
@ -460,15 +460,10 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).accentColor),
|
||||
)
|
||||
: Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
_stringForSeconds(data.item2) ?? '',
|
||||
),
|
||||
Text(
|
||||
' Left',
|
||||
),
|
||||
],
|
||||
: Text(
|
||||
(_stringForSeconds(data.item2) ?? '') +
|
||||
' Left',
|
||||
maxLines: 2,
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -480,10 +475,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
selector: (_, audio) => audio.audioState,
|
||||
builder: (_, audioplay, __) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Spacer(),
|
||||
//Spacer(),
|
||||
audioplay == BasicPlaybackState.playing
|
||||
? InkWell(
|
||||
onTap:
|
||||
@ -518,7 +512,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
)),
|
||||
),
|
||||
Container(
|
||||
height: 50.0,
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.black),
|
||||
@ -531,8 +525,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () => audio.playNext(),
|
||||
iconSize: 25.0,
|
||||
iconSize: 20.0,
|
||||
icon: Icon(Icons.skip_next),
|
||||
color:
|
||||
Theme.of(context).tabBarTheme.labelColor),
|
||||
@ -1251,7 +1246,7 @@ class _ControlPanelState extends State<ControlPanel>
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
onPressed:
|
||||
backplay == BasicPlaybackState.playing
|
||||
? () => audio.forwardAudio(-10)
|
||||
@ -1323,7 +1318,7 @@ class _ControlPanelState extends State<ControlPanel>
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||
onPressed:
|
||||
backplay == BasicPlaybackState.playing
|
||||
? () => audio.forwardAudio(30)
|
||||
|
@ -158,7 +158,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||
controller: _controller,
|
||||
tabs: <Widget>[
|
||||
Tab(
|
||||
child: Text('Recent Update'),
|
||||
child: Text('Recent'),
|
||||
),
|
||||
Tab(
|
||||
child: Text('Favorite'),
|
||||
|
@ -440,7 +440,7 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
SlideLeftRoute(
|
||||
SlideLeftHideRoute(
|
||||
page: PodcastDetail(
|
||||
podcastLocal: widget.podcastLocal,
|
||||
)),
|
||||
|
@ -39,7 +39,7 @@ class Import extends StatelessWidget {
|
||||
groupList.subscribeNewPodcast(item.id);
|
||||
return importColumn("Fetch data ${item.title}", context);
|
||||
case SubscribeState.fetch:
|
||||
groupList.updatePodcast(item.id);
|
||||
// groupList.updatePodcast(item.id);
|
||||
return importColumn("Subscribe success ${item.title}", context);
|
||||
case SubscribeState.exist:
|
||||
return importColumn(
|
||||
|
@ -253,10 +253,8 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
child: _addGroup
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
@ -284,9 +282,10 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
}).toList()),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
@ -374,7 +373,7 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 20,
|
||||
right: 100,
|
||||
right: context.width / 3,
|
||||
bottom: 20),
|
||||
title:
|
||||
Text('Skip seconds at the beginning'),
|
||||
@ -452,7 +451,7 @@ class _PodcastCardState extends State<PodcastCard>
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 20,
|
||||
right: 200,
|
||||
right: context.width / 3,
|
||||
bottom: 20),
|
||||
title: Text('Remove confirm'),
|
||||
content: Text(
|
||||
@ -535,8 +534,8 @@ class _RenameGroupState extends State<RenameGroup> {
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
elevation: 1,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding:
|
||||
EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20),
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20, left: 20, right: context.width / 3, bottom: 20),
|
||||
actionsPadding: EdgeInsets.all(0),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
|
@ -13,6 +13,7 @@ import '../type/podcastlocal.dart';
|
||||
import '../local_storage/sqflite_localpodcast.dart';
|
||||
import '../podcasts/podcastdetail.dart';
|
||||
import '../util/pageroute.dart';
|
||||
import '../util/context_extension.dart';
|
||||
|
||||
class AboutPodcast extends StatefulWidget {
|
||||
final PodcastLocal podcastLocal;
|
||||
@ -48,7 +49,8 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0))),
|
||||
titlePadding: EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20),
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20, left: 20, right: context.width / 3, bottom: 20),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
@ -63,18 +65,21 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
||||
),
|
||||
],
|
||||
title: Text(widget.podcastLocal.title),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
!_load
|
||||
? Center()
|
||||
: _description != null ? Html(data: _description) : Center(),
|
||||
(widget.podcastLocal.author != null)
|
||||
? Text(widget.podcastLocal.author,
|
||||
style: TextStyle(color: Colors.blue))
|
||||
: Center(),
|
||||
],
|
||||
content: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
!_load
|
||||
? Center()
|
||||
: _description != null ? Html(data: _description) : Center(),
|
||||
(widget.podcastLocal.author != null)
|
||||
? Text(widget.podcastLocal.author,
|
||||
style: TextStyle(color: Colors.blue))
|
||||
: Center(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -49,11 +49,11 @@ class _PodcastManageState extends State<PodcastManage>
|
||||
_fraction = _animation.value;
|
||||
});
|
||||
});
|
||||
_menuAnimation = Tween(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _menuController, curve: Curves.easeIn))
|
||||
..addListener(() {
|
||||
if (mounted) setState(() => _menuValue = _menuAnimation.value);
|
||||
});
|
||||
_menuAnimation = Tween(begin: 0.0, end: 1.0)
|
||||
.animate(CurvedAnimation(parent: _menuController, curve: Curves.easeIn))
|
||||
..addListener(() {
|
||||
if (mounted) setState(() => _menuValue = _menuAnimation.value);
|
||||
});
|
||||
|
||||
_controller.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
@ -361,7 +361,9 @@ class _PodcastManageState extends State<PodcastManage>
|
||||
EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 20,
|
||||
right: 200,
|
||||
right: context
|
||||
.width /
|
||||
3,
|
||||
bottom: 20),
|
||||
title: Text(
|
||||
'Delete confirm'),
|
||||
@ -531,8 +533,8 @@ class _AddGroupState extends State<AddGroup> {
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
elevation: 1,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding:
|
||||
EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20),
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20, left: 20, right: context.width / 3, bottom: 20),
|
||||
actionsPadding: EdgeInsets.all(0),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
|
@ -85,7 +85,10 @@ class _SettingsState extends State<Settings>
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 5),
|
||||
),
|
||||
Text(name)
|
||||
Text(
|
||||
name,
|
||||
maxLines: 2,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -284,28 +287,31 @@ class _SettingsState extends State<Settings>
|
||||
),
|
||||
),
|
||||
_showFeedback
|
||||
? Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
_feedbackItem(
|
||||
LineIcons.github,
|
||||
'Submit issue',
|
||||
'https://github.com/stonega/tsacdop/issues'),
|
||||
_feedbackItem(
|
||||
LineIcons.telegram,
|
||||
'Join group',
|
||||
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
|
||||
_feedbackItem(
|
||||
LineIcons.envelope_open_text_solid,
|
||||
'Write to me',
|
||||
'mailto:<tsacdop.app@gmail.com>?subject=Tsacdop Feedback'),
|
||||
_feedbackItem(
|
||||
LineIcons.google_play,
|
||||
'Rate on Play',
|
||||
'https://play.google.com/store/apps/details?id=com.stonegate.tsacdop')
|
||||
],
|
||||
? SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
_feedbackItem(
|
||||
LineIcons.github,
|
||||
'Submit issue',
|
||||
'https://github.com/stonega/tsacdop/issues'),
|
||||
_feedbackItem(
|
||||
LineIcons.telegram,
|
||||
'Join group',
|
||||
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
|
||||
_feedbackItem(
|
||||
LineIcons.envelope_open_text_solid,
|
||||
'Write to me',
|
||||
'mailto:<tsacdop.app@gmail.com>?subject=Tsacdop Feedback'),
|
||||
_feedbackItem(
|
||||
LineIcons.google_play,
|
||||
'Rate on Play',
|
||||
'https://play.google.com/store/apps/details?id=com.stonegate.tsacdop')
|
||||
],
|
||||
),
|
||||
)
|
||||
: Center(),
|
||||
Divider(
|
||||
|
@ -72,45 +72,46 @@ class ThemeSetting extends StatelessWidget {
|
||||
titlePadding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 40,
|
||||
right: 200,
|
||||
right: context.width / 3,
|
||||
),
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10.0))),
|
||||
title: Text('Theme'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
RadioListTile(
|
||||
title: Container(
|
||||
padding:
|
||||
EdgeInsets.only(right: 80),
|
||||
child: Text('System default')),
|
||||
value: ThemeMode.system,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
RadioListTile(
|
||||
title: Text('Dark mode'),
|
||||
value: ThemeMode.dark,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
RadioListTile(
|
||||
title: Text('Light mode'),
|
||||
value: ThemeMode.light,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
],
|
||||
content: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
RadioListTile(
|
||||
title: Text('System default'),
|
||||
value: ThemeMode.system,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
RadioListTile(
|
||||
title: Text('Dark mode'),
|
||||
value: ThemeMode.dark,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
RadioListTile(
|
||||
title: Text('Light mode'),
|
||||
value: ThemeMode.light,
|
||||
groupValue: settings.theme,
|
||||
onChanged: (value) {
|
||||
settings.setTheme = value;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
@ -357,4 +358,4 @@ class _ColorPickerState extends State<ColorPicker>
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class SubscribeWorker extends ChangeNotifier {
|
||||
SubscribeItem _subscribeItem;
|
||||
SubscribeItem _currentSubscribeItem = SubscribeItem('', '');
|
||||
bool _created = false;
|
||||
bool get created=> _created;
|
||||
bool get created => _created;
|
||||
|
||||
setSubscribeItem(SubscribeItem item) async {
|
||||
_subscribeItem = item;
|
||||
@ -117,9 +117,23 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||
receiveTimeout: 20000,
|
||||
);
|
||||
print(rss);
|
||||
|
||||
try {
|
||||
Response response = await Dio(options).get(rss);
|
||||
var p = RssFeed.parse(response.data);
|
||||
RssFeed p;
|
||||
try {
|
||||
p = RssFeed.parse(response.data);
|
||||
} on ArgumentError 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");
|
||||
}
|
||||
|
||||
var dir = await getApplicationDocumentsDirectory();
|
||||
|
||||
@ -128,31 +142,35 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||
|
||||
print(realUrl);
|
||||
bool checkUrl = await dbHelper.checkPodcast(realUrl);
|
||||
|
||||
String imageUrl;
|
||||
if (checkUrl) {
|
||||
img.Image thumbnail;
|
||||
try {
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
p.itunes.image.href,
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
imageUrl = p.itunes.image.href;
|
||||
img.Image image = img.decodeImage(imageResponse.data);
|
||||
thumbnail = img.copyResize(image, width: 300);
|
||||
} on DioError catch (e) {
|
||||
} catch (e) {
|
||||
print(e);
|
||||
try {
|
||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||
item.imgUrl,
|
||||
options: Options(responseType: ResponseType.bytes));
|
||||
imageUrl = item.imgUrl;
|
||||
img.Image image = img.decodeImage(imageResponse.data);
|
||||
thumbnail = img.copyResize(image, width: 300);
|
||||
} on DioError catch (e) {
|
||||
} 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));
|
||||
imageUrl =
|
||||
"https://ui-avatars.com/api/?size=300&background=4D91BE&color=fff&name=${item.title}&length=2&bold=true";
|
||||
thumbnail = img.decodeImage(imageResponse.data);
|
||||
} on DioError catch (e) {
|
||||
} catch (e) {
|
||||
print(e);
|
||||
sendPort.send([item.title, item.url, 6]);
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
@ -174,8 +192,8 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
|
||||
String author = p.itunes.author ?? p.author ?? '';
|
||||
String provider = p.generator ?? '';
|
||||
String link = p.link ?? '';
|
||||
PodcastLocal podcastLocal = PodcastLocal(p.title, p.itunes.image.href,
|
||||
realUrl, primaryColor, author, uuid, imagePath, provider, link,
|
||||
PodcastLocal podcastLocal = PodcastLocal(p.title, imageUrl, realUrl,
|
||||
primaryColor, author, uuid, imagePath, provider, link,
|
||||
description: p.description);
|
||||
|
||||
// await groupList.subscribe(podcastLocal);
|
||||
|
@ -30,7 +30,8 @@ class SlideLeftRoute extends PageRouteBuilder {
|
||||
|
||||
class SlideLeftHideRoute extends PageRouteBuilder {
|
||||
final Widget page;
|
||||
SlideLeftHideRoute({this.page})
|
||||
final Widget transitionPage;
|
||||
SlideLeftHideRoute({this.page, this.transitionPage})
|
||||
: super(
|
||||
pageBuilder: (
|
||||
BuildContext context,
|
||||
@ -38,25 +39,23 @@ class SlideLeftHideRoute extends PageRouteBuilder {
|
||||
Animation<double> secondaryAnimation,
|
||||
) =>
|
||||
page,
|
||||
transitionDuration: Duration(seconds: 2),
|
||||
transitionsBuilder: (
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,
|
||||
) =>
|
||||
SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(1, 0),
|
||||
end: Offset.zero,
|
||||
).animate(animation),
|
||||
child: Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: SizedBox(
|
||||
width: context.width,
|
||||
height: context.height,
|
||||
child: child),
|
||||
),
|
||||
),
|
||||
) {
|
||||
if (animation.isCompleted)
|
||||
return child;
|
||||
else
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(1, 0),
|
||||
end: Offset.zero,
|
||||
).animate(animation),
|
||||
child: transitionPage);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
name: tsacdop
|
||||
description: An easy-use podacasts player.
|
||||
|
||||
version: 0.3.1
|
||||
version: 0.3.2
|
||||
|
||||
environment:
|
||||
sdk: ">=2.6.0 <3.0.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user