0.5.8 - queue reordering, bug and ui fixes, shortcut icon

This commit is contained in:
exttex 2020-10-16 20:54:04 +02:00
parent 396b51e90f
commit 9e18537b0c
39 changed files with 219 additions and 27 deletions

View File

@ -293,6 +293,7 @@ public class Deezer {
isFlac = false;
}
Tag tag = f.getTag();
tag.setEncoding("utf-8");
tag.setField(FieldKey.TITLE, publicTrack.getString("title"));
tag.setField(FieldKey.ALBUM, publicTrack.getJSONObject("album").getString("title"));

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_favorites_background"/>
<foreground android:drawable="@mipmap/ic_favorites_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_favorites_background"/>
<foreground android:drawable="@mipmap/ic_favorites_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_flow_background"/>
<foreground android:drawable="@mipmap/ic_flow_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_flow_background"/>
<foreground android:drawable="@mipmap/ic_flow_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_favorites_background">#3DDC84</color>
</resources>

View File

@ -49,6 +49,9 @@ class Cache {
@JsonKey(ignore: true)
StreamSubscription sleepTimer;
@JsonKey(defaultValue: const [])
List<String> searchHistory;
//If download threads warning was shown
@JsonKey(defaultValue: false)
bool threadsWarning;

View File

@ -31,6 +31,8 @@ Cache _$CacheFromJson(Map<String, dynamic> json) {
PlaylistSortType.DEFAULT
..trackSort = _$enumDecodeNullable(_$SortTypeEnumMap, json['trackSort']) ??
SortType.DEFAULT
..searchHistory =
(json['searchHistory'] as List)?.map((e) => e as String)?.toList() ?? []
..threadsWarning = json['threadsWarning'] as bool ?? false;
}
@ -44,6 +46,7 @@ Map<String, dynamic> _$CacheToJson(Cache instance) => <String, dynamic>{
'libraryPlaylistSort':
_$PlaylistSortTypeEnumMap[instance.libraryPlaylistSort],
'trackSort': _$SortTypeEnumMap[instance.trackSort],
'searchHistory': instance.searchHistory,
'threadsWarning': instance.threadsWarning,
};

View File

@ -221,7 +221,7 @@ class PlayerHelper {
QueueSource queueSource = QueueSource(
id: stl.id,
source: (stl.id == 'flow')?'flow':'smarttracklist',
text: stl.title
text: stl.title??(stl.id == 'flow')?'Flow'.i18n:'Smart track list'.i18n
);
await playFromTrackList(stl.tracks, stl.tracks[0].id, queueSource);
}
@ -233,6 +233,11 @@ class PlayerHelper {
await AudioService.customAction('queueSource', queueSource.toJson());
}
//Reorder tracks in queue
Future reorder(int oldIndex, int newIndex) async {
await AudioService.customAction('reorder', [oldIndex, newIndex]);
}
}
void backgroundTaskEntrypoint() async {
@ -562,6 +567,18 @@ class AudioPlayerTask extends BackgroundAudioTask {
if (name == 'screenAndroidAuto' && _androidAutoCallback != null) {
_androidAutoCallback.complete(jsonDecode(args).map<MediaItem>((m) => MediaItem.fromJson(m)).toList());
}
//Reorder tracks, args = [old, new]
if (name == 'reorder') {
await _audioSource.move(args[0], args[1]);
//Switch in queue
List<MediaItem> newQueue = List.from(_queue);
newQueue.removeAt(args[0]);
newQueue.insert(args[1], _queue[args[0]]);
_queue = newQueue;
//Update UI
AudioServiceBackground.setQueue(_queue);
_broadcastState();
}
return true;
}

File diff suppressed because one or more lines are too long

View File

@ -245,6 +245,9 @@ const language_en_us = {
"Minutes:": "Minutes:",
"Hours:": "Hours:",
"Cancel current timer": "Cancel current timer",
"Current timer ends at": "Current timer ends at"
"Current timer ends at": "Current timer ends at",
//0.5.8 Strings:
"Smart track list": "Smart track list"
}
};

View File

@ -14,6 +14,7 @@ import 'package:freezer/ui/search.dart';
import 'package:i18n_extension/i18n_widget.dart';
import 'package:move_to_background/move_to_background.dart';
import 'package:freezer/translations.i18n.dart';
import 'package:quick_actions/quick_actions.dart';
import 'package:uni_links/uni_links.dart';
import 'api/deezer.dart';
@ -51,6 +52,7 @@ class _FreezerAppState extends State<FreezerApp> {
void initState() {
//Make update theme global
updateTheme = _updateTheme;
_updateTheme();
super.initState();
}
@ -63,6 +65,9 @@ class _FreezerAppState extends State<FreezerApp> {
setState(() {
settings.themeData;
});
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: settings.themeData.bottomAppBarColor,
));
}
Locale _locale() {
@ -158,28 +163,47 @@ class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateM
void initState() {
navigatorKey = GlobalKey<NavigatorState>();
//Setup URLs
setupUniLinks();
//Start with parameters
_setupUniLinks();
_loadPreloadInfo();
_prepareQuickActions();
super.initState();
}
void _prepareQuickActions() {
final QuickActions quickActions = QuickActions();
quickActions.initialize((type) {
if (type != null)
_startPreload(type);
});
//Actions
quickActions.setShortcutItems([
ShortcutItem(type: 'favorites', localizedTitle: 'Favorites'.i18n, icon: 'ic_favorites'),
ShortcutItem(type: 'flow', localizedTitle: 'Flow'.i18n, icon: 'ic_flow'),
]);
}
void _startPreload(String type) async {
await deezerAPI.authorize();
if (type == 'flow') {
await playerHelper.playFromSmartTrackList(SmartTrackList(id: 'flow'));
return;
}
if (type == 'favorites') {
Playlist p = await deezerAPI.fullPlaylist(deezerAPI.favoritesPlaylistId);
playerHelper.playFromPlaylist(p, p.tracks[0].id);
}
}
void _loadPreloadInfo() async {
String info = await DownloadManager.platform.invokeMethod('getPreloadInfo');
if (info != null) {
//Used if started from android auto
await deezerAPI.authorize();
if (info == 'flow') {
await playerHelper.playFromSmartTrackList(SmartTrackList(id: 'flow'));
return;
}
if (info == 'favorites') {
Playlist p = await deezerAPI.fullPlaylist(deezerAPI.favoritesPlaylistId);
playerHelper.playFromPlaylist(p, p.tracks[0].id);
}
_startPreload(info);
}
}
@ -190,7 +214,7 @@ class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateM
super.dispose();
}
void setupUniLinks() async {
void _setupUniLinks() async {
//Listen to URLs
_urlLinkStream = getUriLinksStream().listen((Uri uri) {
openScreenByURL(context, uri.toString());

View File

@ -560,6 +560,7 @@ class _SleepTimerDialogState extends State<SleepTimerDialog> {
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
mainAxisSize: MainAxisSize.min,

View File

@ -1,5 +1,6 @@
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:freezer/settings.dart';
import '../api/player.dart';
@ -46,7 +47,12 @@ class PlayerBar extends StatelessWidget {
Container(
color: Theme.of(context).bottomAppBarColor,
child: ListTile(
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => PlayerScreen())),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => PlayerScreen()));
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: settings.themeData.scaffoldBackgroundColor,
));
},
leading: CachedImage(
width: 50,
height: 50,

View File

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:audio_service/audio_service.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/screenutil.dart';
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/player.dart';
import 'package:freezer/settings.dart';
import 'package:freezer/translations.i18n.dart';
import 'package:freezer/ui/menu.dart';
import 'package:freezer/ui/settings_screen.dart';
@ -25,6 +27,13 @@ class PlayerScreen extends StatefulWidget {
class _PlayerScreenState extends State<PlayerScreen> {
@override
void dispose() {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: settings.themeData.bottomAppBarColor,
));
super.dispose();
}
@override
Widget build(BuildContext context) {
@ -578,7 +587,7 @@ class PlayerScreenTopRow extends StatelessWidget {
Container(
width: this.textWidth??ScreenUtil().setWidth(550),
child: Text(
(short??false)?playerHelper.queueSource.text:'Playing from:'.i18n + ' ' + playerHelper.queueSource.text,
(short??false)?(playerHelper.queueSource.text??''):'Playing from:'.i18n + ' ' + (playerHelper.queueSource.text??''),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
@ -779,9 +788,13 @@ class _QueueScreenState extends State<QueueScreen> {
)
],
),
body: ListView.builder(
itemCount: AudioService.queue.length,
itemBuilder: (context, i) {
body: ReorderableListView(
onReorder: (int oldIndex, int newIndex) async {
if (oldIndex == playerHelper.queueIndex) return;
await playerHelper.reorder(oldIndex, newIndex);
setState(() {});
},
children: List.generate(AudioService.queue.length, (int i) {
Track t = Track.fromMediaItem(AudioService.queue[i]);
return TrackTile(
t,
@ -789,12 +802,9 @@ class _QueueScreenState extends State<QueueScreen> {
await AudioService.playFromMediaId(t.id);
Navigator.of(context).pop();
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(t);
},
key: Key(t.id),
);
},
}),
)
);
}

View File

@ -1,5 +1,6 @@
import 'package:connectivity/connectivity.dart';
import 'package:flutter/material.dart';
import 'package:freezer/api/cache.dart';
import 'package:freezer/api/download.dart';
import 'package:freezer/api/player.dart';
import 'package:freezer/ui/details_screens.dart';
@ -63,6 +64,10 @@ class _SearchScreenState extends State<SearchScreen> {
return;
}
//Add to search history
try {cache.searchHistory.remove(_query);} catch (_) {}
cache.searchHistory.add(_query);
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SearchResultsScreen(_query, offline: _offline,))
);
@ -158,6 +163,19 @@ class _SearchScreenState extends State<SearchScreen> {
if (_loading)
LinearProgressIndicator(),
Divider(),
//History
if (cache.searchHistory.length > 0 && (_query??'').length == 0)
...List.generate(cache.searchHistory.length > 10 ? 10 : cache.searchHistory.length, (int i) => ListTile(
title: Text(cache.searchHistory[i]),
leading: Icon(Icons.history),
onTap: () {
setState(() => _query = cache.searchHistory[i]);
_submit(context);
},
)),
//Suggestions
...List.generate((_suggestions??[]).length, (i) => ListTile(
title: Text(_suggestions[i]),
leading: Icon(Icons.search),

View File

@ -714,6 +714,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
quick_actions:
dependency: "direct main"
description:
name: quick_actions
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0+10"
quiver:
dependency: transitive
description:

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.5.7+1
version: 0.5.8+1
environment:
sdk: ">=2.8.0 <3.0.0"
@ -69,6 +69,7 @@ dependencies:
uni_links: ^0.4.0
share: ^0.6.5+2
numberpicker: ^1.2.1
quick_actions: ^0.4.0+10
audio_session: ^0.0.9
audio_service: