From 64dee98523ace4107ad59e915838bb4068778dd8 Mon Sep 17 00:00:00 2001 From: stonegate Date: Sun, 22 Mar 2020 00:14:10 +0800 Subject: [PATCH] Back to saty in background Playlist UI change --- .circleci/config.yml | 2 +- android/app/build.gradle | 6 +- .../com/stonegate/tsacdop/MainActivity.kt | 14 + .../res/drawable-hdpi/ic_notification.png | Bin 725 -> 633 bytes .../res/drawable-mdpi/ic_notification.png | Bin 427 -> 390 bytes .../res/drawable-xhdpi/ic_notification.png | Bin 822 -> 758 bytes .../res/drawable-xxhdpi/ic_notification.png | Bin 1472 -> 1310 bytes .../res/drawable-xxxhdpi/ic_notification.png | Bin 1790 -> 1635 bytes android/build.gradle | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- lib/class/settingstate.dart | 3 +- lib/episodes/episodedetail.dart | 33 +- lib/home/appbar/addpodcast.dart | 13 +- lib/home/home.dart | 2 +- .../{homescroll.dart => home_groups.dart} | 2 +- lib/home/playlist.dart | 382 ++++++++++++------ lib/local_storage/sqflite_localpodcast.dart | 2 +- lib/main.dart | 16 - lib/podcasts/podcastdetail.dart | 44 +- lib/podcasts/podcastgroup.dart | 81 ++-- lib/settings/downloads_manage.dart | 12 +- lib/settings/settting.dart | 40 +- lib/settings/storage.dart | 1 + lib/settings/syncing.dart | 1 + lib/settings/theme.dart | 1 + lib/util/episodegrid.dart | 21 +- lib/util/mypopupmenu.dart | 4 + lib/webfeed/domain/rss_item.dart | 5 +- pubspec.yaml | 3 +- 29 files changed, 447 insertions(+), 249 deletions(-) rename lib/home/{homescroll.dart => home_groups.dart} (99%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 69642c2..d84e8a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: cirrusci/flutter:v1.14.6 + - image: cirrusci/flutter:v1.15.17 branches: only: master diff --git a/android/app/build.gradle b/android/app/build.gradle index 9914def..c1db363 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -79,7 +79,7 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + testImplementation 'junit:junit:4.13' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/android/app/src/main/kotlin/com/stonegate/tsacdop/MainActivity.kt b/android/app/src/main/kotlin/com/stonegate/tsacdop/MainActivity.kt index 615f563..a2ab3b0 100644 --- a/android/app/src/main/kotlin/com/stonegate/tsacdop/MainActivity.kt +++ b/android/app/src/main/kotlin/com/stonegate/tsacdop/MainActivity.kt @@ -3,10 +3,24 @@ package com.stonegate.tsacdop import androidx.annotation.NonNull; import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine +import io.flutter.view.FlutterNativeView import io.flutter.plugins.GeneratedPluginRegistrant +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.embedding.engine.dart.DartExecutor +import io.flutter.embedding.engine.dart.DartExecutor.DartCallback class MainActivity: FlutterActivity() { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); + + MethodChannel(flutterEngine.dartExecutor, "android_app_retain").apply { + setMethodCallHandler { method, result -> + if (method.method == "sendToBackground") { + moveTaskToBack(true) + } + } + } } } diff --git a/android/app/src/main/res/drawable-hdpi/ic_notification.png b/android/app/src/main/res/drawable-hdpi/ic_notification.png index 94ea1cdb908dcf14197556dfbd11a1bad2e216ae..9c381ed713fd0b3cea24fa422ff905e65c8ec6a3 100644 GIT binary patch delta 608 zcmV-m0-ycW1^EP!B!56jL_t(oN9~wRh>lSd$A5pxcfzn>HrTObF)K96ntWvzQi__j z4c3a%OvYv+GiyXxO|v53l$jF3LbDK3gkmC`=}h;n>GeGC^WOVDQ=^`{ea^Y(_x{g0 z_uTu68MZXTc=*47%xo606xa=H1C{}AfUCe=U|iDgGF8r1$bZZh0y}^c!0Ni72D}2U z0e67sz`HWw8Ub|yJ;2HW1%3bzfhWKa@L1CKRMj$p%xoU;0$3BVv8V2fD+LSymnD6O z@Silz1KJB*irp;3eh|0_JOe&S`c<@N9?*H0)`+ByQ9w0gGg}TE2iAs} z=5Vd1@`9v)j+UrR&CH8@53mB*2XwR`*A_{)lN73V3V*5ewmks&=^NXPSAedpVH~B< zloT!k`hbH|V1EX%k3qq#diw+12M$Ym+4MP7lm;`)0;;)f26h2ULV6d35BGpaz&S~; zqee%IK<&VLU@p)J_|3uBQ(#PztCYLSOj#{RyClDYB&`alNVZC$N+F=?GQXBII;mGl z@`EvHrCD~FUqAd2*_{!!P15Hin$$Z@c=PWE&V-p&i+|n!2Phg3rH>BZ(9C87+x-Or ucnWU-rzL$$Rmc63Qr1eqBHs$ANTCU0OTQ(Vjvsaa00002|ShZDFQ5vgQf zRtNM7j$%V!J@OsS;4|FD-@O3V0lkI8c&1OF$!335em0Kl_&Jq~h*Y(s)d78k_h$*y z+B~Q9M|_QoIe(@2sS{Wo&?)S05U91QRP7s_#zaK?(>_ie&=HKx5m0vf06*XW&PGID zPgxz%Fg|Wu>P_<{#Fy9`5#M(MdJV_0zG;fK{_bKpB2IP#dK&LxAC}B9)bWVOnAdsA z@<}Nr17bq$!m2rh8jFY>U4bg7Mc7syFXM#w5SQp$WR#R{a%@8GFffMs$XiHJ}80%}n#L&Zj%M=pT0yw^)@ zmFZ)&TK2!r3aIk0SMe3HOnG>#I`TZDZ&nPQ)XPfXQYst~*V<>Jm#>;?TS{pMni3EP ztBcpM9Dj?eAe*3n;#(Ywh#PgT(KJwwu`~Uml=56v!mP!D3MR9D)=QaiYi%xoh9V;U zqc0l(b(3wN&_E#!R$10dog#I*$Uvb~XkE3(${!G|mPz)Fi1@tQYHB)lB|dfZ+WYPnUuHeDv4 jiE4eNj+u1a?>qki07c{_E_I&y00000NkvXXu0mjfOYl@j diff --git a/android/app/src/main/res/drawable-mdpi/ic_notification.png b/android/app/src/main/res/drawable-mdpi/ic_notification.png index 1ca81587edadbdbcfc92f878cbd083a6f36182b3..50afe4d4078e790f468a6dc58474dafc95f7d117 100644 GIT binary patch delta 363 zcmV-x0hIo$1BL^TB!5jwL_t(YOYM|DC`Dly#($4Vp}Sd)7KJh*lfh;&Q5XyiCbRu! zL@ECYW!23fvy_GGhHJ9OqDbo1$GP=6zH`ra+|G92@$|gU@9jD7`-zfECHoTunAsSx z3=9Adz$tJk>AC1Ye*k8-1ndNi3^)XKfm=y$F|c0%GphhkK!0aM^Ez+>)Fpjp>C=D( zU_Zj#mwW*8KuywXx;G721F8i9zI<5Ho&O;X7;^cx0C)j<{m4Q;`2v`10kSCRpwS-! z^Z?ty>^~tJl3di^m3e4p)(1=gn?QE~*qo%}T!3$AW^TLonhlibAAx>J??nMFuA`*M ztz`|E20FY!>SVx-q?^nw%8J3ve1MaU1J}R`Falh;3lAM!)MFO_j7oB*xzz@=Bl=0e z%%*^IFONY<_qq8*TxJ8%1#Bgc;2E$g$sJs7MXhPBOA|rqfG@Reb{QP=ic$ao002ov JPDHLkV1n6Gq_zM6 delta 401 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 JPDHLkV1gNDu$%w@ diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_notification.png b/android/app/src/main/res/drawable-xxxhdpi/ic_notification.png index 9e501f48362fae155090a9b3db4d6cedb9eeedc6..bafa892ea983740a0593f91bde08e5478098a393 100644 GIT binary patch literal 1635 zcmchY>s!)y1Bbt$p!~Lm`lMiFYJyI39;%_PG&4fcP5|?5c(f}xWm#_9DN4& zNKzAcgX18s!OoeW?6I}Z_7yb2h{F(G?CI~RdRY0F)G=R`m0<68KeFfbS1Mu8#N^T! zfB9uTOOG5&Qm-r;S#A2_=li0V3X;I)n zZda-)09Y$!;-9D9xu@sP!XS$4+ zg}{F=i)b;61N#U)Q~WJA=W3W|dom9L>`!&Mw}DRJLEiW-Gc|EC3a%LC#H}DbyG9tD zjHxcEp1+3Q68*@n8A$^v53($bjnYsJHR+>-9h`|;Ar%7o@Y>r?J<6|gA9t*#=t?za zZ$AbnW8Ouapb}y{36?wia2maE1MUP@!;2+6oyHbt?uS={moXSe*n}=m? zEgl@EzPlfsK8xi=c5x-O1C@KJlT|C z>cRuNu#<92pKyw%wHcmPn*X2dZ}CgT)irTI+b%4^&M*lmNyza{gr-EWC)6_cL*5jB zgkt*i7;a|EJyPCy5te;$`3ze5x%EAdK#Zyq^*!wT>A_BJF7s%UmDls1h-4QPwj8lc z%4UQEzc#(T1RVkaINtzFmd(V(2b`O$nS6K#EnxoMPyb6SfNK^{Jmu8FxqV zo7AzvM=m^}jQrzq1Iz;D_h(o~Ce8%c6aKCmk`7A45ef7^wr*~aeuBQh5pwXq_ zka66b^^JQVT-zdSFwr6xA?CQ;+;^-Hb1+0ug}4-^aZe+v9dky;HMQ{Ciu8-?c6HC3 z&6;z;x#-bR^q1?;m9dKXOM9-~^C*rM^f|E-jzbLd(x&!xtzmeXHK+UFjx1E8&zc=V zGR#%58^I#<`ce_G9zT|a%eDkaWLDJxt>pE!4KPzCx|xct;?>VtXa?NZ9@YA!S8k^Gi+P|`Fo$|;=ceAGmoA#M)D zPy5c%UFM&}Rc!^_5_!v%KaVl#x^wBLf1;Hb*S9a)7ukLJyQE_aUu386Py^=(37}v4 z>~gDG$`1S^#;eWI%*4Z*C-z3LevUHP{MJo2UBFv;M_jUmsuN<$G!G-6e)nbQ>m7y-dVLj~Jg+r3Pc@VF+E0A*69LIXY{YLf#&j z`^F;}p8af;$&Hd_+-S7NU2_>izq6)?A$9U;%$bdPb9Sqwte!f^&XNP?Ag}hTie_5Q zUQLyAqWe&v@nt>bFz<<-!SW`7CoZt{m%VwpU>HOvgcjWO9xe+ggfSXF_2DJ+dtjKE zfeWc!>#asGWjHMYy?8Xw*Hq{EHXz5d?v6Hu!w6#UFJznVn66PthPK?ms7C`5RneVga z+b)=w;fMJ<6p~=M@g|3eV2>==+wF7hF()8zz^vG=J#)7vfcY6Dua|dDYyeWgc=2(u zk{AtsCvlS8g4!;xv79p10708BwLaH`XTy_LF_w>lCK8OrAiu0J{1Or#tjM#!-@4@~ zHUfM4&t@S}Pv7#ENZkFFrAK}a-m`l=jQ4UYid2twgJZL< zIaxoD?o8oxhg+t*%y<8!X#A9;v z@(~h%9EXlv*NLRJKcs%ys)TT#Y@gKKQ$>oIubve~+SWVZ>7%3lTh1=MA-|HafKAd* z010;7rtEK-r9_u(B2jN*zbHGl7jS8oF$fvYqm& zcc-M9Bzhm{a6I%?mILXaOcHlUXR<2O%H{}?)KC>y}3 zQ!nq#2bWzuJ6YUXSRR&0kr{-?!Eq<8*EvrGwAJ1PPp5; z&U_}m!}0r@{=h3~sBlDcD(cI~T6A-glYsBM2j^`Ja4vKyENfz8P+zStIPY&}5&*`d zl#l*kc3wSLafUs(Ljm>iTLwm;1)xf3aV=p#+&%$Iv@B7#?7%94NDIgM9euDn)$2LW8a3;Hll-%7RJL~U^TP3h1+CCRwont zPItnW^&dJ+*ikVpZcFIC*phNmcW#<*m*)!krC@0fsbx0}Pac7UX_;t^wz-elraGUxGyW@{}Hj8Lx%PoMitCd8{bMZcWr z`i`yQ^y;Y(S&j~{a(rWohbNwj8mY@Pg0ljK@|!LH*T|5d*7rF*=fm@McMb44bi|{< H4W#`8&eufV diff --git a/android/build.gradle b/android/build.gradle index 232bc0d..09661db 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.3.70' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:3.6.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 296b146..c79d946 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 +#Fri Mar 20 23:46:20 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/lib/class/settingstate.dart b/lib/class/settingstate.dart index 4d606f7..b0c17c4 100644 --- a/lib/class/settingstate.dart +++ b/lib/class/settingstate.dart @@ -9,7 +9,6 @@ import 'package:tsacdop/local_storage/key_value_storage.dart'; void callbackDispatcher() { Workmanager.executeTask((task, inputData) async { var dbHelper = DBHelper(); - print('Start task'); List podcastList = await dbHelper.getPodcastLocalAll(); int i = 0; await Future.forEach(podcastList, (podcastLocal) async { @@ -135,7 +134,7 @@ class SettingState extends ChangeNotifier { Future _getUpdateInterval() async { _initUpdateTag = await intervalstorage.getInt(); - _updateInterval = _initUpdateTag == 0 ? 24 : _initUpdateTag; + _updateInterval = _initUpdateTag; } Future _saveUpdateInterval() async { diff --git a/lib/episodes/episodedetail.dart b/lib/episodes/episodedetail.dart index 24b1261..59562b3 100644 --- a/lib/episodes/episodedetail.dart +++ b/lib/episodes/episodedetail.dart @@ -11,6 +11,8 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:intl/intl.dart'; import 'package:tuple/tuple.dart'; import 'package:audio_service/audio_service.dart'; +import 'package:flutter_linkify/flutter_linkify.dart'; + import 'package:tsacdop/class/audiostate.dart'; import 'package:tsacdop/class/episodebrief.dart'; import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; @@ -31,10 +33,11 @@ class _EpisodeDetailState extends State { bool _loaddes; bool _showMenu; String path; + String _description; Future getSDescription(String url) async { var dbHelper = DBHelper(); - widget.episodeItem.description = (await dbHelper.getDescription(url)) - .replaceAll(RegExp(r'\s?

(
)?

\s?'), ''); + _description = (await dbHelper.getDescription(url)) + .replaceAll(RegExp(r'\s?

(
)?

\s?'), '').replaceAll('\r', ''); if (mounted) setState(() { _loaddes = true; @@ -85,12 +88,11 @@ class _EpisodeDetailState extends State { systemNavigationBarColor: Theme.of(context).primaryColor, systemNavigationBarIconBrightness: Theme.of(context).accentColorBrightness, - // statusBarColor: Theme.of(context).primaryColor, ), child: Scaffold( backgroundColor: Theme.of(context).primaryColor, appBar: AppBar( - title: Text(widget.episodeItem.feedTitle), + // title: Text(widget.episodeItem.feedTitle), centerTitle: true, ), body: Stack( @@ -162,7 +164,8 @@ class _EpisodeDetailState extends State { style: textstyle), ) : Center(), - widget.episodeItem.enclosureLength != null + widget.episodeItem.enclosureLength != null && + widget.episodeItem.enclosureLength != 0 ? Container( decoration: BoxDecoration( color: Colors.lightBlue[300], @@ -193,15 +196,15 @@ class _EpisodeDetailState extends State { padding: EdgeInsets.only(top: 5.0), child: SingleChildScrollView( scrollDirection: Axis.vertical, - //physics: const AlwaysScrollableScrollPhysics(), + physics: AlwaysScrollableScrollPhysics(), controller: _controller, child: _loaddes - ? (widget.episodeItem.description.contains('<')) + ? (_description.contains('<')) ? Html( padding: EdgeInsets.symmetric(horizontal: 20.0), defaultTextStyle: TextStyle(height: 1.8), - data: widget.episodeItem.description, + data: _description, linkStyle: TextStyle( color: Theme.of(context).accentColor, decoration: TextDecoration.underline, @@ -215,12 +218,20 @@ class _EpisodeDetailState extends State { padding: EdgeInsets.symmetric(horizontal: 20.0), alignment: Alignment.topLeft, - child: Text( - widget.episodeItem.description, + child: SelectableLinkify( + onOpen: (link) { + _launchUrl(link.url); + }, + text: _description, style: TextStyle( height: 1.8, ), - )) + linkStyle: TextStyle( + color: Theme.of(context).accentColor, + decoration: TextDecoration.underline, + ), + ), + ) : Center(), ), ), diff --git a/lib/home/appbar/addpodcast.dart b/lib/home/appbar/addpodcast.dart index f9acd8f..0f8533c 100644 --- a/lib/home/appbar/addpodcast.dart +++ b/lib/home/appbar/addpodcast.dart @@ -32,6 +32,8 @@ class _MyHomePageState extends State { final _MyHomePageDelegate _delegate = _MyHomePageDelegate(); final GlobalKey _scaffoldKey = GlobalKey(); + var _androidAppRetain = MethodChannel("android_app_retain"); + @override void initState() { super.initState(); @@ -71,7 +73,16 @@ class _MyHomePageState extends State { PopupMenu(), ], ), - body: Home(), + body: WillPopScope( + onWillPop: () async { + if (Platform.isAndroid) { + _androidAppRetain.invokeMethod('sendToBackground'); + return false; + } else { + return true; + } + }, + child: Home()), ), ); } diff --git a/lib/home/home.dart b/lib/home/home.dart index cc2cf43..691d0dc 100644 --- a/lib/home/home.dart +++ b/lib/home/home.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'hometab.dart'; import 'package:tsacdop/home/appbar/importompl.dart'; import 'package:tsacdop/home/audioplayer.dart'; -import 'homescroll.dart'; +import 'home_groups.dart'; class Home extends StatelessWidget { diff --git a/lib/home/homescroll.dart b/lib/home/home_groups.dart similarity index 99% rename from lib/home/homescroll.dart rename to lib/home/home_groups.dart index e58d345..d7a49cc 100644 --- a/lib/home/homescroll.dart +++ b/lib/home/home_groups.dart @@ -40,7 +40,7 @@ class _ScrollPodcastsState extends State { bool isLoading = groupList.isLoading; return isLoading ? Container( - height: (_width - 20) / 3 + 110, + height: (_width - 20) / 3 + 140, ) : groups[_groupIndex].podcastList.length == 0 ? Column( diff --git a/lib/home/playlist.dart b/lib/home/playlist.dart index 6be4361..b643a9f 100644 --- a/lib/home/playlist.dart +++ b/lib/home/playlist.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -6,6 +7,7 @@ import 'package:provider/provider.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:tsacdop/episodes/episodedetail.dart'; import 'package:tuple/tuple.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:line_icons/line_icons.dart'; import 'package:tsacdop/class/audiostate.dart'; import 'package:tsacdop/class/episodebrief.dart'; @@ -19,7 +21,8 @@ class PlaylistPage extends StatefulWidget { class _PlaylistPageState extends State { final GlobalKey _playlistKey = GlobalKey(); final textstyle = TextStyle(fontSize: 15.0, color: Colors.black); - Widget episodeTag(String text, Color color) { + + Widget _episodeTag(String text, Color color) { return Container( decoration: BoxDecoration( color: color, borderRadius: BorderRadius.all(Radius.circular(15.0))), @@ -31,6 +34,40 @@ class _PlaylistPageState extends State { ); } + int _sumPlaylistLength(List episodes) { + int sum = 0; + if (episodes.length == 0) { + return sum; + } else { + episodes.forEach((episode) { + sum += episode.duration; + }); + return sum; + } + } + + ScrollController _controller; + _scrollListener() { + double value = _controller.offset; + setState(() => _topHeight = (100 - value) > 0 ? 100 - value : 0); + } + + double _topHeight; + + @override + void initState() { + super.initState(); + _topHeight = 100; + _controller = ScrollController(); + _controller.addListener(_scrollListener); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { var audio = Provider.of(context, listen: false); @@ -42,8 +79,9 @@ class _PlaylistPageState extends State { systemNavigationBarColor: Theme.of(context).primaryColor, ), child: Scaffold( + backgroundColor: Theme.of(context).primaryColor, appBar: AppBar( - title: Text('Playlist'), + title: _topHeight == 0 ? Text('Playlist') : Center(), elevation: 0, backgroundColor: Theme.of(context).primaryColor, ), @@ -53,137 +91,225 @@ class _PlaylistPageState extends State { selector: (_, audio) => Tuple2(audio.queue.playlist, audio.playerRunning), builder: (_, data, __) { - return AnimatedList( - key: _playlistKey, - shrinkWrap: true, - scrollDirection: Axis.vertical, - initialItemCount: data.item1.length, - itemBuilder: (context, index, animation) { - Color _c = - (Theme.of(context).brightness == Brightness.light) - ? data.item1[index].primaryColor.colorizedark() - : data.item1[index].primaryColor.colorizeLight(); - return ScaleTransition( - alignment: Alignment.centerLeft, - scale: animation, - child: Dismissible( - background: Container( - padding: EdgeInsets.symmetric(horizontal: 20.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Icon( - Icons.delete, - color: Theme.of(context).accentColor, - ), - Icon( - Icons.delete, - color: Theme.of(context).accentColor, - ), - ], + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Transform.scale( + alignment: Alignment.topLeft, + scale: _topHeight / 100, + child: Container( + height: _topHeight, + padding: EdgeInsets.only( + bottom: (_topHeight - 60) > 0 ? _topHeight - 60 : 0, + left: 60), + alignment: Alignment.bottomLeft, + child: RichText( + text: TextSpan( + text: 'Total ', + style: TextStyle( + color: Theme.of(context).accentColor, + fontSize: 20, ), - height: 50, - color: Colors.grey[500], - ), - key: Key(data.item1[index].enclosureUrl), - onDismissed: (direction) async { - await audio.delFromPlaylist(data.item1[index]); - _playlistKey.currentState.removeItem( - index, (context, animation) => Center()); - Fluttertoast.showToast( - msg: 'Removed From Playlist', - gravity: ToastGravity.BOTTOM, - ); - }, - child: Column( - children: [ - ListTile( - title: Text( - data.item1[index].title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - leading: CircleAvatar( - backgroundColor: _c.withOpacity(0.5), - backgroundImage: FileImage( - File("${data.item1[index].imagePath}")), - ), - trailing: index == 0 - ? data.item2 - ? Padding( - padding: const EdgeInsets.only( - right: 12.0), - child: SizedBox( - width: 20, - height: 15, - child: WaveLoader()), - ) - : IconButton( - icon: Icon(Icons.play_arrow), - onPressed: () => audio.playlistLoad()) - : IconButton( - tooltip: 'Move to Top', - icon: - Icon(LineIcons.arrow_circle_up_solid), - onPressed: () async { - await audio - .moveToTop(data.item1[index]); - _playlistKey.currentState.removeItem( - index, - (context, animation) => - Container()); - data.item2 - ? _playlistKey.currentState - .insertItem(1) - : _playlistKey.currentState - .insertItem(0); - }), - subtitle: Container( - padding: EdgeInsets.symmetric(vertical: 5), - child: Row( - children: [ - (data.item1[index].explicit == 1) - ? Container( - decoration: BoxDecoration( - color: Colors.red[800], - shape: BoxShape.circle), - height: 20.0, - width: 20.0, - margin: - EdgeInsets.only(right: 10.0), - alignment: Alignment.center, - child: Text('E', - style: TextStyle( - color: Colors.white))) - : Center(), - data.item1[index].duration != 0 - ? episodeTag( - (data.item1[index].duration) - .toString() + - 'mins', - Colors.cyan[300]) - : Center(), - data.item1[index].enclosureLength != null - ? episodeTag( - ((data.item1[index] - .enclosureLength) ~/ - 1000000) - .toString() + - 'MB', - Colors.lightBlue[300]) - : Center(), - ], - ), - ), - ), - Divider( - height: 2, + children: [ + TextSpan( + text: data.item1.length.toString(), + style: TextStyle( + color: Theme.of(context).accentColor, + fontSize: 40, + )), + TextSpan( + text: data.item1.length < 2 + ? ' episode' + : ' episodes ', + style: TextStyle( + color: Theme.of(context).accentColor, + fontSize: 20, + )), + TextSpan( + text: _sumPlaylistLength(data.item1).toString(), + style: GoogleFonts.teko( + textStyle: TextStyle( + color: Theme.of(context).accentColor, + fontSize: 60, + )), ), + TextSpan( + text: ' mins', + style: TextStyle( + color: Theme.of(context).accentColor, + fontSize: 20, + )), ], ), ), - ); - }); + ), + ), + Expanded( + child: AnimatedList( + controller: _controller, + key: _playlistKey, + shrinkWrap: true, + scrollDirection: Axis.vertical, + initialItemCount: data.item1.length, + itemBuilder: (context, index, animation) { + Color _c = (Theme.of(context).brightness == + Brightness.light) + ? data.item1[index].primaryColor.colorizedark() + : data.item1[index].primaryColor.colorizeLight(); + return ScaleTransition( + alignment: Alignment.centerLeft, + scale: animation, + child: Dismissible( + background: Container( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red), + padding: EdgeInsets.all(5), + child: Icon( + LineIcons.trash_alt_solid, + color: Colors.white, + size: 15, + ), + ), + Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red), + padding: EdgeInsets.all(5), + child: Icon( + LineIcons.trash_alt_solid, + color: Colors.white, + size: 15, + ), + ), + ], + ), + height: 50, + color: Theme.of(context).accentColor, + ), + key: Key(data.item1[index].enclosureUrl), + onDismissed: (direction) async { + await audio.delFromPlaylist(data.item1[index]); + _playlistKey.currentState.removeItem( + index, (context, animation) => Center()); + Fluttertoast.showToast( + msg: 'Removed From Playlist', + gravity: ToastGravity.BOTTOM, + ); + }, + child: Column( + children: [ + ListTile( + title: Container( + padding: EdgeInsets.only( + top: 10.0, bottom: 5.0), + child: Text( + data.item1[index].title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + leading: CircleAvatar( + backgroundColor: _c.withOpacity(0.5), + backgroundImage: FileImage(File( + "${data.item1[index].imagePath}")), + ), + trailing: index == 0 + ? data.item2 + ? Padding( + padding: const EdgeInsets.only( + right: 12.0), + child: SizedBox( + width: 20, + height: 15, + child: WaveLoader()), + ) + : IconButton( + icon: Icon(Icons.play_arrow), + onPressed: () => + audio.playlistLoad()) + : Transform.rotate( + angle: math.pi, + child: IconButton( + tooltip: 'Move to Top', + icon: Icon( + LineIcons.download_solid), + onPressed: () async { + await audio.moveToTop( + data.item1[index]); + _playlistKey.currentState + .removeItem( + index, + (context, + animation) => + Container()); + data.item2 + ? _playlistKey + .currentState + .insertItem(1) + : _playlistKey + .currentState + .insertItem(0); + }), + ), + subtitle: Container( + padding: + EdgeInsets.only(top: 5, bottom: 10), + child: Row( + children: [ + (data.item1[index].explicit == 1) + ? Container( + decoration: BoxDecoration( + color: Colors.red[800], + shape: BoxShape.circle), + height: 20.0, + width: 20.0, + margin: EdgeInsets.only( + right: 10.0), + alignment: Alignment.center, + child: Text('E', + style: TextStyle( + color: Colors.white))) + : Center(), + data.item1[index].duration != 0 + ? _episodeTag( + (data.item1[index].duration) + .toString() + + 'mins', + Colors.cyan[300]) + : Center(), + data.item1[index].enclosureLength != + null + ? _episodeTag( + ((data.item1[index] + .enclosureLength) ~/ + 1000000) + .toString() + + 'MB', + Colors.lightBlue[300]) + : Center(), + ], + ), + ), + ), + Divider( + height: 2, + ), + ], + ), + ), + ); + }), + ), + ], + ); }, ), ), diff --git a/lib/local_storage/sqflite_localpodcast.dart b/lib/local_storage/sqflite_localpodcast.dart index 4ece98b..f7c54c8 100644 --- a/lib/local_storage/sqflite_localpodcast.dart +++ b/lib/local_storage/sqflite_localpodcast.dart @@ -417,7 +417,7 @@ class DBHelper { } final title = feed.items[i].itunes.title ?? feed.items[i].title; - final length = feed.items[i]?.enclosure?.length; + final length = feed.items[i]?.enclosure?.length ?? 0; final pubDate = feed.items[i].pubDate; final date = _parsePubDate(pubDate); final milliseconds = date.millisecondsSinceEpoch; diff --git a/lib/main.dart b/lib/main.dart index 4147eb6..ee2dad5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,28 +2,12 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter/services.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; -import 'package:workmanager/workmanager.dart'; -import 'package:tsacdop/class/podcastlocal.dart'; import 'package:tsacdop/class/podcast_group.dart'; import 'package:tsacdop/home/appbar/addpodcast.dart'; import 'package:tsacdop/class/audiostate.dart'; import 'package:tsacdop/class/importompl.dart'; import 'package:tsacdop/class/settingstate.dart'; -import 'package:tsacdop/local_storage/sqflite_localpodcast.dart'; - -void callbackDispatcher() { - Workmanager.executeTask((task, inputData) async { - var dbHelper = DBHelper(); - print('Start task'); - List podcastList = await dbHelper.getPodcastLocalAll(); - await Future.forEach(podcastList, (podcastLocal) async { - await dbHelper.updatePodcastRss(podcastLocal); - print('Refresh ' + podcastLocal.title); - }); - return Future.value(true); - }); -} final SettingState themeSetting = SettingState(); Future main() async { diff --git a/lib/podcasts/podcastdetail.dart b/lib/podcasts/podcastdetail.dart index 16b2e9c..5b1a83e 100644 --- a/lib/podcasts/podcastdetail.dart +++ b/lib/podcasts/podcastdetail.dart @@ -6,8 +6,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:html/parser.dart'; import 'package:url_launcher/url_launcher.dart'; - import 'package:fluttertoast/fluttertoast.dart'; +import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:tsacdop/class/podcastlocal.dart'; @@ -389,6 +389,14 @@ class _AboutPodcastState extends State { setState(() => _load = true); } + _launchUrl(String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + throw 'Could not launch $url'; + } + } + @override void initState() { super.initState(); @@ -419,8 +427,15 @@ class _AboutPodcastState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - _description, + Linkify( + onOpen: (link) { + _launchUrl(link.url); + }, + text: _description, + linkStyle: TextStyle( + color: Theme.of(context).accentColor, + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic), maxLines: 3, overflow: TextOverflow.ellipsis, ), @@ -432,12 +447,27 @@ class _AboutPodcastState extends State { ), ], ) - : Text(_description), + : Linkify( + onOpen: (link) { + _launchUrl(link.url); + }, + text: _description, + linkStyle: TextStyle( + color: Theme.of(context).accentColor, + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic), + ), ); } else { - return SelectableText( - _description, - toolbarOptions: ToolbarOptions(copy: true), + return Linkify( + text: _description, + onOpen: (link) { + _launchUrl(link.url); + }, + linkStyle: TextStyle( + color: Theme.of(context).accentColor, + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic), ); } }, diff --git a/lib/podcasts/podcastgroup.dart b/lib/podcasts/podcastgroup.dart index 15fc98d..c3ba3b3 100644 --- a/lib/podcasts/podcastgroup.dart +++ b/lib/podcasts/podcastgroup.dart @@ -301,50 +301,47 @@ class _PodcastCardState extends State { Brightness.light ? Color.fromRGBO(113, 113, 113, 1) : Color.fromRGBO(15, 15, 15, 1), - statusBarColor: - Theme.of(context).brightness == - Brightness.light - ? Color.fromRGBO(113, 113, 113, 1) - : Color.fromRGBO(5, 5, 5, 1), + // statusBarColor: + // Theme.of(context).brightness == + // Brightness.light + // ? Color.fromRGBO(113, 113, 113, 1) + // : Color.fromRGBO(5, 5, 5, 1), ), - child: SafeArea( - child: AlertDialog( - elevation: 1, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10.0))), - titlePadding: EdgeInsets.only( - top: 20, - left: 20, - right: 200, - bottom: 20), - title: Text('Remove confirm'), - content: Text( - 'Are you sure you want to unsubscribe?'), - actions: [ - FlatButton( - onPressed: () => - Navigator.of(context).pop(), - child: Text( - 'CANCEL', - style: TextStyle( - color: Colors.grey[600]), - ), + child: AlertDialog( + elevation: 1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10.0))), + titlePadding: EdgeInsets.only( + top: 20, + left: 20, + right: 200, + bottom: 20), + title: Text('Remove confirm'), + content: Text( + 'Are you sure you want to unsubscribe?'), + actions: [ + FlatButton( + onPressed: () => + Navigator.of(context).pop(), + child: Text( + 'CANCEL', + style: TextStyle( + color: Colors.grey[600]), ), - FlatButton( - onPressed: () { - _groupList.removePodcast( - widget.podcastLocal.id); - Navigator.of(context).pop(); - }, - child: Text( - 'CONFIRM', - style: - TextStyle(color: Colors.red), - ), - ) - ], - ), + ), + FlatButton( + onPressed: () { + _groupList.removePodcast( + widget.podcastLocal.id); + Navigator.of(context).pop(); + }, + child: Text( + 'CONFIRM', + style: TextStyle(color: Colors.red), + ), + ) + ], ), ), ); diff --git a/lib/settings/downloads_manage.dart b/lib/settings/downloads_manage.dart index fad7582..543e893 100644 --- a/lib/settings/downloads_manage.dart +++ b/lib/settings/downloads_manage.dart @@ -136,9 +136,9 @@ class _DownloadsManageState extends State { TextSpan( text: _fileNum.toString(), style: TextStyle( - color: Theme.of(context).accentColor, - fontSize: 40, - fontWeight: FontWeight.bold)), + color: Theme.of(context).accentColor, + fontSize: 40, + )), TextSpan( text: _fileNum < 2 ? ' episode' : ' episodes ', style: TextStyle( @@ -148,9 +148,9 @@ class _DownloadsManageState extends State { TextSpan( text: (_size ~/ 1000000).toString(), style: TextStyle( - color: Theme.of(context).accentColor, - fontSize: 60, - fontWeight: FontWeight.bold)), + color: Theme.of(context).accentColor, + fontSize: 60, + )), TextSpan( text: ' Mb', style: TextStyle( diff --git a/lib/settings/settting.dart b/lib/settings/settting.dart index cdb3c38..2c45ec2 100644 --- a/lib/settings/settting.dart +++ b/lib/settings/settting.dart @@ -59,8 +59,8 @@ class Settings extends StatelessWidget { backgroundColor: Theme.of(context).primaryColor, ), body: SafeArea( - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), + child: SingleChildScrollView( + //physics: const AlwaysScrollableScrollPhysics(), scrollDirection: Axis.vertical, child: Column( mainAxisAlignment: MainAxisAlignment.start, @@ -93,14 +93,16 @@ class Settings extends StatelessWidget { context, MaterialPageRoute( builder: (context) => ThemeSetting())), - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(LineIcons.adjust_solid), title: Text('Appearance'), subtitle: Text('Colors and themes'), ), Divider(height: 2), ListTile( - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(LineIcons.play_circle), title: Text('AutoPlay'), subtitle: Text('Autoplay next episode in playlist'), @@ -117,7 +119,8 @@ class Settings extends StatelessWidget { context, MaterialPageRoute( builder: (context) => SyncingSetting())), - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(LineIcons.cloud_download_alt_solid), title: Text('Syncing'), subtitle: Text('Refresh podcasts in the background'), @@ -128,7 +131,8 @@ class Settings extends StatelessWidget { context, MaterialPageRoute( builder: (context) => StorageSetting())), - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(LineIcons.save), title: Text('Storage'), subtitle: Text('Manage cache and download storage'), @@ -139,7 +143,8 @@ class Settings extends StatelessWidget { context, MaterialPageRoute( builder: (context) => PlayedHistory())), - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(Icons.update), title: Text('History'), subtitle: Text('Listen data'), @@ -149,7 +154,8 @@ class Settings extends StatelessWidget { onTap: () { _exportOmpl(); }, - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(LineIcons.file_code_solid), title: Text('Export'), subtitle: Text('Export ompl file'), @@ -184,25 +190,31 @@ class Settings extends StatelessWidget { ListTile( onTap: () => _launchUrl( 'https://github.com/stonega/tsacdop/releases'), - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(LineIcons.map_signs_solid), title: Text('Changelog'), subtitle: Text('List of chagnes'), ), Divider(height: 2), ListTile( - onTap: () => Navigator.push(context, - MaterialPageRoute(builder: (context) => Libries())), - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Libries())), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(LineIcons.book_open_solid), title: Text('Libraries'), - subtitle: Text('Open source libraries in application'), + subtitle: + Text('Open source libraries in application'), ), Divider(height: 2), ListTile( onTap: () => _launchUrl( 'mailto:?subject=Tsacdop Feedback'), - contentPadding: EdgeInsets.symmetric(horizontal: 25.0), + contentPadding: + EdgeInsets.symmetric(horizontal: 25.0), leading: Icon(LineIcons.bug_solid), title: Text('Feedback'), subtitle: Text('Bugs and feature requests'), diff --git a/lib/settings/storage.dart b/lib/settings/storage.dart index 5db52f4..fbc4ca1 100644 --- a/lib/settings/storage.dart +++ b/lib/settings/storage.dart @@ -42,6 +42,7 @@ class StorageSetting extends StatelessWidget { .copyWith(color: Theme.of(context).accentColor)), ), ListView( + physics: const BouncingScrollPhysics(), shrinkWrap: true, scrollDirection: Axis.vertical, children: [ diff --git a/lib/settings/syncing.dart b/lib/settings/syncing.dart index a0e1246..36441ac 100644 --- a/lib/settings/syncing.dart +++ b/lib/settings/syncing.dart @@ -48,6 +48,7 @@ class SyncingSetting extends StatelessWidget { .copyWith(color: Theme.of(context).accentColor)), ), ListView( + physics: const BouncingScrollPhysics(), shrinkWrap: true, scrollDirection: Axis.vertical, children: [ diff --git a/lib/settings/theme.dart b/lib/settings/theme.dart index 3fc58ba..23decd1 100644 --- a/lib/settings/theme.dart +++ b/lib/settings/theme.dart @@ -43,6 +43,7 @@ class ThemeSetting extends StatelessWidget { .copyWith(color: Theme.of(context).accentColor)), ), ListView( + physics: const BouncingScrollPhysics(), shrinkWrap: true, scrollDirection: Axis.vertical, children: [ diff --git a/lib/util/episodegrid.dart b/lib/util/episodegrid.dart index 40e677c..5b4f1dd 100644 --- a/lib/util/episodegrid.dart +++ b/lib/util/episodegrid.dart @@ -31,7 +31,7 @@ class EpisodeGrid extends StatelessWidget { this.heroTag, this.updateCount = 0}) : super(key: key); - + @override Widget build(BuildContext context) { double _width = MediaQuery.of(context).size.width; @@ -46,7 +46,6 @@ class EpisodeGrid extends StatelessWidget { borderRadius: BorderRadius.all(Radius.circular(10))), context: context, position: RelativeRect.fromLTRB(left, top, _width - left, 0), - items: >[ PopupMenuItem( value: 0, @@ -85,7 +84,7 @@ class EpisodeGrid extends StatelessWidget { if (value == 0) { if (!isPlaying) audio.episodeLoad(episode); } else if (value == 1) { - if (!isInPlaylist) { + if (!isInPlaylist) { audio.addToPlaylist(episode); Fluttertoast.showToast( msg: 'Added to playlist', @@ -97,7 +96,7 @@ class EpisodeGrid extends StatelessWidget { msg: 'Removed from playlist', gravity: ToastGravity.BOTTOM, ); - } + } } }); } @@ -187,7 +186,15 @@ class EpisodeGrid extends StatelessWidget { ), ), Spacer(), - index < updateCount ? Text('New', style: TextStyle(color: Colors.red, fontStyle: FontStyle.italic)) : Center(), + index < updateCount + ? Text('New', + style: TextStyle( + color: Colors.red, + fontStyle: FontStyle.italic)) + : Center(), + Padding( + padding: EdgeInsets.symmetric(horizontal: 2), + ), showNumber ? Container( alignment: Alignment.topRight, @@ -371,7 +378,7 @@ class _DownloadIconState extends State { child: CircularProgressIndicator( backgroundColor: Colors.grey[200], strokeWidth: 1, - valueColor: AlwaysStoppedAnimation(Colors.blue), + valueColor: AlwaysStoppedAnimation(Theme.of(context).accentColor), value: task.progress / 100, ), ); @@ -391,7 +398,7 @@ class _DownloadIconState extends State { data: IconThemeData(size: 15), child: Icon( Icons.done_all, - color: Colors.blue, + color: Theme.of(context).accentColor, ), ); } else if (task.status == DownloadTaskStatus.failed) { diff --git a/lib/util/mypopupmenu.dart b/lib/util/mypopupmenu.dart index 25bcc87..dfdc6ec 100644 --- a/lib/util/mypopupmenu.dart +++ b/lib/util/mypopupmenu.dart @@ -347,6 +347,8 @@ Future _showMenu({ break; case TargetPlatform.android: case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.windows: label = semanticLabel ?? MaterialLocalizations.of(context)?.popupMenuLabel; } @@ -469,6 +471,8 @@ class MyPopupMenuButtonState extends State> { return const Icon(Icons.more_vert); case TargetPlatform.iOS: case TargetPlatform.macOS: + case TargetPlatform.linux: + case TargetPlatform.windows: return const Icon(Icons.more_horiz); } return null; diff --git a/lib/webfeed/domain/rss_item.dart b/lib/webfeed/domain/rss_item.dart index 75aed0e..e96428c 100644 --- a/lib/webfeed/domain/rss_item.dart +++ b/lib/webfeed/domain/rss_item.dart @@ -49,8 +49,7 @@ class RssItem { } return RssItem( title: findElementOrNull(element, "title")?.text, - description: findElementOrNull(element, "description")?.text?.trim() - , + description: findElementOrNull(element, "description")?.text?.trim(), link: findElementOrNull(element, "link")?.text?.trim(), categories: element.findElements("category").map((element) { return RssCategory.parse(element); @@ -60,7 +59,7 @@ class RssItem { author: findElementOrNull(element, "author")?.text?.trim(), // comments: findElementOrNull(element, "comments")?.text, // source: RssSource.parse(findElementOrNull(element, "source")), - content: RssContent.parse(findElementOrNull(element, "content:encoded")), + content: RssContent.parse(findElementOrNull(element, "content:encoded")), // media: Media.parse(element), enclosure: RssEnclosure.parse(findElementOrNull(element, "enclosure")), //dc: DublinCore.parse(element), diff --git a/pubspec.yaml b/pubspec.yaml index 41ffc58..aa681ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,7 +28,7 @@ dev_dependencies: flutter_test: sdk: flutter json_annotation: ^3.0.1 - sqflite: ^1.2.2+1 + sqflite: ^1.3.0 flutter_html: ^0.11.1 path_provider: ^1.6.1 color_thief_flutter: ^1.0.1 @@ -59,6 +59,7 @@ dev_dependencies: git: url: https://github.com/galonsos/line_icons.git flutter_file_dialog: ^0.0.5 + flutter_linkify: ^3.1.0 # For information on the generic Dart part of this file, see the