0.6.1 - UI Fixes

This commit is contained in:
exttex 2020-10-20 21:55:14 +02:00
parent 1384aedb35
commit df3b7d3d63
16 changed files with 215 additions and 34 deletions

View File

@ -49,8 +49,10 @@ class Cache {
@JsonKey(ignore: true) @JsonKey(ignore: true)
StreamSubscription sleepTimer; StreamSubscription sleepTimer;
@JsonKey(defaultValue: []) //Search history
List<String> searchHistory; @JsonKey(name: 'searchHistory2', toJson: _searchHistoryToJson, fromJson: _searchHistoryFromJson)
List<SearchHistoryItem> searchHistory;
//If download threads warning was shown //If download threads warning was shown
@JsonKey(defaultValue: false) @JsonKey(defaultValue: false)
@ -65,6 +67,23 @@ class Cache {
return libraryTracks.contains(t.id); return libraryTracks.contains(t.id);
} }
//Add to history
void addToSearchHistory(dynamic item) async {
if (searchHistory == null)
searchHistory = [];
if (item is Track)
searchHistory.add(SearchHistoryItem(item, SearchHistoryItemType.TRACK));
if (item is Album)
searchHistory.add(SearchHistoryItem(item, SearchHistoryItemType.ALBUM));
if (item is Artist)
searchHistory.add(SearchHistoryItem(item, SearchHistoryItemType.ARTIST));
if (item is Playlist)
searchHistory.add(SearchHistoryItem(item, SearchHistoryItemType.PLAYLIST));
await save();
}
//Save, load //Save, load
static Future<String> getPath() async { static Future<String> getPath() async {
return p.join((await getApplicationDocumentsDirectory()).path, 'metacache.json'); return p.join((await getApplicationDocumentsDirectory()).path, 'metacache.json');
@ -89,4 +108,45 @@ class Cache {
//JSON //JSON
factory Cache.fromJson(Map<String, dynamic> json) => _$CacheFromJson(json); factory Cache.fromJson(Map<String, dynamic> json) => _$CacheFromJson(json);
Map<String, dynamic> toJson() => _$CacheToJson(this); Map<String, dynamic> toJson() => _$CacheToJson(this);
//Search History JSON
static List<SearchHistoryItem> _searchHistoryFromJson(List<dynamic> json) {
return (json??[]).map<SearchHistoryItem>((i) => _searchHistoryItemFromJson(i)).toList();
}
static SearchHistoryItem _searchHistoryItemFromJson(Map<String, dynamic> json) {
SearchHistoryItemType type = SearchHistoryItemType.values[json['type']];
dynamic data;
switch (type) {
case SearchHistoryItemType.TRACK:
data = Track.fromJson(json['data']);
break;
case SearchHistoryItemType.ALBUM:
data = Album.fromJson(json['data']);
break;
case SearchHistoryItemType.ARTIST:
data = Artist.fromJson(json['data']);
break;
case SearchHistoryItemType.PLAYLIST:
data = Playlist.fromJson(json['data']);
break;
}
return SearchHistoryItem(data, type);
}
static List<Map<String, dynamic>> _searchHistoryToJson(List<SearchHistoryItem> data) => (data??[]).map<Map<String, dynamic>>((i) => {"type": i.type.index, "data": i.data.toJson()}).toList();
}
@JsonSerializable()
class SearchHistoryItem {
dynamic data;
SearchHistoryItemType type;
SearchHistoryItem(this.data, this.type);
}
enum SearchHistoryItemType {
TRACK,
ALBUM,
ARTIST,
PLAYLIST
} }

View File

@ -32,7 +32,7 @@ Cache _$CacheFromJson(Map<String, dynamic> json) {
..trackSort = _$enumDecodeNullable(_$SortTypeEnumMap, json['trackSort']) ?? ..trackSort = _$enumDecodeNullable(_$SortTypeEnumMap, json['trackSort']) ??
SortType.DEFAULT SortType.DEFAULT
..searchHistory = ..searchHistory =
(json['searchHistory'] as List)?.map((e) => e as String)?.toList() ?? [] Cache._searchHistoryFromJson(json['searchHistory2'] as List)
..threadsWarning = json['threadsWarning'] as bool ?? false; ..threadsWarning = json['threadsWarning'] as bool ?? false;
} }
@ -46,7 +46,7 @@ Map<String, dynamic> _$CacheToJson(Cache instance) => <String, dynamic>{
'libraryPlaylistSort': 'libraryPlaylistSort':
_$PlaylistSortTypeEnumMap[instance.libraryPlaylistSort], _$PlaylistSortTypeEnumMap[instance.libraryPlaylistSort],
'trackSort': _$SortTypeEnumMap[instance.trackSort], 'trackSort': _$SortTypeEnumMap[instance.trackSort],
'searchHistory': instance.searchHistory, 'searchHistory2': Cache._searchHistoryToJson(instance.searchHistory),
'threadsWarning': instance.threadsWarning, 'threadsWarning': instance.threadsWarning,
}; };
@ -110,3 +110,23 @@ const _$PlaylistSortTypeEnumMap = {
PlaylistSortType.USER: 'USER', PlaylistSortType.USER: 'USER',
PlaylistSortType.TRACK_COUNT: 'TRACK_COUNT', PlaylistSortType.TRACK_COUNT: 'TRACK_COUNT',
}; };
SearchHistoryItem _$SearchHistoryItemFromJson(Map<String, dynamic> json) {
return SearchHistoryItem(
json['data'],
_$enumDecodeNullable(_$SearchHistoryItemTypeEnumMap, json['type']),
);
}
Map<String, dynamic> _$SearchHistoryItemToJson(SearchHistoryItem instance) =>
<String, dynamic>{
'data': instance.data,
'type': _$SearchHistoryItemTypeEnumMap[instance.type],
};
const _$SearchHistoryItemTypeEnumMap = {
SearchHistoryItemType.TRACK: 'TRACK',
SearchHistoryItemType.ALBUM: 'ALBUM',
SearchHistoryItemType.ARTIST: 'ARTIST',
SearchHistoryItemType.PLAYLIST: 'PLAYLIST',
};

View File

@ -115,7 +115,7 @@ class DeezerAPI {
//Search //Search
Future<SearchResults> search(String query) async { Future<SearchResults> search(String query) async {
Map<dynamic, dynamic> data = await callApi('deezer.pageSearch', params: { Map<dynamic, dynamic> data = await callApi('deezer.pageSearch', params: {
'nb': 50, 'nb': 128,
'query': query, 'query': query,
'start': 0 'start': 0
}); });

View File

@ -456,7 +456,7 @@ class AudioPlayerTask extends BackgroundAudioTask {
MediaAction.seekTo, MediaAction.seekTo,
MediaAction.seekForward, MediaAction.seekForward,
MediaAction.seekBackward, MediaAction.seekBackward,
//MediaAction.stop MediaAction.stop
], ],
processingState: _getProcessingState(), processingState: _getProcessingState(),
playing: _player.playing, playing: _player.playing,

File diff suppressed because one or more lines are too long

View File

@ -255,6 +255,10 @@ const language_en_us = {
"Library shuffle": "Library shuffle", "Library shuffle": "Library shuffle",
"Ignore interruptions": "Ignore interruptions", "Ignore interruptions": "Ignore interruptions",
"Requires app restart to apply!": "Requires app restart to apply!", "Requires app restart to apply!": "Requires app restart to apply!",
"Ask before downloading": "Ask before downloading" "Ask before downloading": "Ask before downloading",
//0.6.1 Strings:
"Search history": "Search history",
"Clear search history": "Clear search history"
} }
}; };

View File

@ -67,7 +67,7 @@ class _FreezerAppState extends State<FreezerApp> {
}); });
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: settings.themeData.bottomAppBarColor, systemNavigationBarColor: settings.themeData.bottomAppBarColor,
systemNavigationBarIconBrightness: (settings.theme == Themes.Light)?Brightness.dark:Brightness.light systemNavigationBarIconBrightness: settings.isDark? Brightness.light : Brightness.dark
)); ));
} }

View File

@ -187,12 +187,23 @@ class Settings {
return 8; //default return 8; //default
} }
//Check if is dark, can't use theme directly, because of system themes, and Theme.of(context).brightness broke
bool get isDark {
if (useSystemTheme) {
if (SchedulerBinding.instance.window.platformBrightness == Brightness.light) return false;
return true;
}
if (theme == Themes.Light) return false;
return true;
}
static const deezerBg = Color(0xFF1F1A16); static const deezerBg = Color(0xFF1F1A16);
static const deezerBottom = Color(0xFF1b1714); static const deezerBottom = Color(0xFF1b1714);
static const font = 'MabryPro'; static const font = 'MabryPro';
Map<Themes, ThemeData> get _themeData => { Map<Themes, ThemeData> get _themeData => {
Themes.Light: ThemeData( Themes.Light: ThemeData(
fontFamily: font, fontFamily: font,
brightness: Brightness.light,
primaryColor: primaryColor, primaryColor: primaryColor,
accentColor: primaryColor, accentColor: primaryColor,
sliderTheme: _sliderTheme, sliderTheme: _sliderTheme,
@ -235,8 +246,9 @@ class Settings {
sliderTheme: _sliderTheme, sliderTheme: _sliderTheme,
toggleableActiveColor: primaryColor, toggleableActiveColor: primaryColor,
bottomSheetTheme: BottomSheetThemeData( bottomSheetTheme: BottomSheetThemeData(
backgroundColor: Colors.black backgroundColor: Colors.black,
)) )
)
}; };
Future<String> getPath() async => p.join((await getApplicationDocumentsDirectory()).path, 'settings.json'); Future<String> getPath() async => p.join((await getApplicationDocumentsDirectory()).path, 'settings.json');

View File

@ -23,6 +23,8 @@ const supportedLocales = [
const Locale('pl', 'PL'), const Locale('pl', 'PL'),
const Locale('uk', 'UA'), const Locale('uk', 'UA'),
const Locale('hu', 'HU'), const Locale('hu', 'HU'),
const Locale('ur', 'PK'),
const Locale('hi', 'IN'),
const Locale('fil', 'PH') const Locale('fil', 'PH')
]; ];

View File

@ -78,6 +78,6 @@ class FreezerDivider extends StatelessWidget {
TextStyle popupMenuTextStyle() { TextStyle popupMenuTextStyle() {
return TextStyle( return TextStyle(
color: (settings.theme == Themes.Light)?Colors.black:Colors.white color: settings.isDark?Colors.white:Colors.black
); );
} }

View File

@ -273,7 +273,7 @@ class HomePageItemWidget extends StatelessWidget {
onTap: () { onTap: () {
Navigator.of(context).push(MaterialPageRoute( Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Scaffold( builder: (context) => Scaffold(
appBar: AppBar(title: Text(item.value.title.toString()),), appBar: FreezerAppBar(item.value.title.toString()),
body: SingleChildScrollView( body: SingleChildScrollView(
child: HomePageScreen(channel: item.value,) child: HomePageScreen(channel: item.value,)
), ),

View File

@ -1031,11 +1031,11 @@ class _HistoryScreenState extends State<HistoryScreen> {
body: ListView.builder( body: ListView.builder(
itemCount: (cache.history??[]).length, itemCount: (cache.history??[]).length,
itemBuilder: (BuildContext context, int i) { itemBuilder: (BuildContext context, int i) {
Track t = cache.history[i]; Track t = cache.history[cache.history.length - i - 1];
return TrackTile( return TrackTile(
t, t,
onTap: () { onTap: () {
playerHelper.playFromTrackList(cache.history, t.id, QueueSource( playerHelper.playFromTrackList(cache.history.reversed.toList(), t.id, QueueSource(
id: null, id: null,
text: 'History'.i18n, text: 'History'.i18n,
source: 'history' source: 'history'

View File

@ -66,13 +66,6 @@ class _SearchScreenState extends State<SearchScreen> {
return; return;
} }
//Add to search history
try {cache.searchHistory.remove(_query);} catch (_) {}
if (cache.searchHistory == null)
cache.searchHistory = [];
cache.searchHistory.insert(0, _query);
cache.save();
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SearchResultsScreen(_query, offline: _offline,)) MaterialPageRoute(builder: (context) => SearchResultsScreen(_query, offline: _offline,))
); );
@ -80,7 +73,7 @@ class _SearchScreenState extends State<SearchScreen> {
@override @override
void initState() { void initState() {
_cancel = true; _cancel = false;
//Check for connectivity and enable offline mode //Check for connectivity and enable offline mode
Connectivity().checkConnectivity().then((res) { Connectivity().checkConnectivity().then((res) {
if (res == ConnectivityResult.none) setState(() { if (res == ConnectivityResult.none) setState(() {
@ -102,12 +95,24 @@ class _SearchScreenState extends State<SearchScreen> {
List sugg; List sugg;
try { try {
sugg = await deezerAPI.searchSuggestions(_query); sugg = await deezerAPI.searchSuggestions(_query);
} catch (e) {} } catch (e) {print(e);}
if (sugg != null && !_cancel) if (sugg != null && !_cancel)
setState(() => _suggestions = sugg); setState(() => _suggestions = sugg);
} }
Widget _removeHistoryItemWidget(int index) {
return IconButton(
icon: Icon(Icons.close),
onPressed: () async {
if (cache.searchHistory != null)
cache.searchHistory.removeAt(index);
setState((){});
await cache.save();
}
);
}
@override @override
void dispose() { void dispose() {
_cancel = true; _cancel = true;
@ -136,6 +141,8 @@ class _SearchScreenState extends State<SearchScreen> {
}, },
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Search or paste URL'.i18n, labelText: 'Search or paste URL'.i18n,
fillColor: Theme.of(context).bottomAppBarColor,
filled: true,
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey) borderSide: BorderSide(color: Colors.grey)
), ),
@ -189,15 +196,84 @@ class _SearchScreenState extends State<SearchScreen> {
FreezerDivider(), FreezerDivider(),
//History //History
if (cache.searchHistory != null && cache.searchHistory.length > 0 && (_query??'').length == 0) if (cache.searchHistory != null && cache.searchHistory.length > 0 && (_query??'').length < 2)
...List.generate(cache.searchHistory.length > 10 ? 10 : cache.searchHistory.length, (int i) => ListTile( ...List.generate(cache.searchHistory.length > 10 ? 10 : cache.searchHistory.length, (int i) {
title: Text(cache.searchHistory[i]??''), dynamic data = cache.searchHistory[i].data;
leading: Icon(Icons.history), switch (cache.searchHistory[i].type) {
case SearchHistoryItemType.TRACK:
return TrackTile(
data,
onTap: () {
List<Track> queue = cache.searchHistory.where((h) => h.type == SearchHistoryItemType.TRACK).map<Track>((t) => t.data).toList();
playerHelper.playFromTrackList(queue, queue.first.id, QueueSource(
text: 'Search history'.i18n,
source: 'searchhistory',
id: 'searchhistory'
));
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(data);
},
trailing: _removeHistoryItemWidget(i),
);
case SearchHistoryItemType.ALBUM:
return AlbumTile(
data,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => AlbumDetails(data))
);
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultAlbumMenu(data);
},
trailing: _removeHistoryItemWidget(i),
);
case SearchHistoryItemType.ARTIST:
return ArtistHorizontalTile(
data,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => ArtistDetails(data))
);
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultArtistMenu(data);
},
trailing: _removeHistoryItemWidget(i),
);
case SearchHistoryItemType.PLAYLIST:
return PlaylistTile(
data,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => PlaylistDetails(data))
);
},
onHold: () {
MenuSheet m = MenuSheet(context);
m.defaultPlaylistMenu(data);
},
trailing: _removeHistoryItemWidget(i),
);
}
return Container();
}),
//Clear history
if (cache.searchHistory != null && cache.searchHistory.length > 2)
ListTile(
title: Text('Clear search history'.i18n),
leading: Icon(Icons.clear_all),
onTap: () { onTap: () {
setState(() => _query = cache.searchHistory[i]); cache.searchHistory = [];
_submit(context); cache.save();
setState((){});
}, },
)), ),
//Suggestions //Suggestions
...List.generate((_suggestions??[]).length, (i) => ListTile( ...List.generate((_suggestions??[]).length, (i) => ListTile(
@ -278,6 +354,7 @@ class SearchResultsScreen extends StatelessWidget {
return TrackTile( return TrackTile(
t, t,
onTap: () { onTap: () {
cache.addToSearchHistory(t);
playerHelper.playFromTrackList(results.tracks, t.id, QueueSource( playerHelper.playFromTrackList(results.tracks, t.id, QueueSource(
text: 'Search'.i18n, text: 'Search'.i18n,
id: query, id: query,
@ -331,8 +408,9 @@ class SearchResultsScreen extends StatelessWidget {
m.defaultAlbumMenu(a); m.defaultAlbumMenu(a);
}, },
onTap: () { onTap: () {
cache.addToSearchHistory(a);
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute(builder: (context) => AlbumDetails(a)) MaterialPageRoute(builder: (context) => AlbumDetails(a))
); );
}, },
); );
@ -373,6 +451,7 @@ class SearchResultsScreen extends StatelessWidget {
return ArtistTile( return ArtistTile(
a, a,
onTap: () { onTap: () {
cache.addToSearchHistory(a);
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute(builder: (context) => ArtistDetails(a)) MaterialPageRoute(builder: (context) => ArtistDetails(a))
); );
@ -410,6 +489,7 @@ class SearchResultsScreen extends StatelessWidget {
return PlaylistTile( return PlaylistTile(
p, p,
onTap: () { onTap: () {
cache.addToSearchHistory(p);
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute(builder: (context) => PlaylistDetails(p)) MaterialPageRoute(builder: (context) => PlaylistDetails(p))
); );

View File

@ -1124,6 +1124,7 @@ class _CreditsScreenState extends State<CreditsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: FreezerAppBar('About'.i18n),
body: ListView( body: ListView(
children: [ children: [
FreezerTitle(), FreezerTitle(),

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. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.6.0+1 version: 0.6.1+1
environment: environment:
sdk: ">=2.8.0 <3.0.0" sdk: ">=2.8.0 <3.0.0"

View File

@ -20,7 +20,9 @@ lang_crowdin = {
'tr': 'tr_tr', 'tr': 'tr_tr',
'pl': 'pl_pl', 'pl': 'pl_pl',
'uk': 'uk_ua', 'uk': 'uk_ua',
'hu': 'hu_hu' 'hu': 'hu_hu',
'ur-PK': 'ur_pk',
'hi': 'hi_in'
} }
def generate_dart(): def generate_dart():