diff --git a/CHALLENGE_2/sleepysound/android/app/build.gradle.kts b/CHALLENGE_2/sleepysound/android/app/build.gradle.kts index e2da798..7e83ad8 100644 --- a/CHALLENGE_2/sleepysound/android/app/build.gradle.kts +++ b/CHALLENGE_2/sleepysound/android/app/build.gradle.kts @@ -8,7 +8,7 @@ plugins { android { namespace = "com.example.sleepysound" compileSdk = flutter.compileSdkVersion - ndkVersion = flutter.ndkVersion + ndkVersion = "27.0.12077973" compileOptions { sourceCompatibility = JavaVersion.VERSION_11 diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..704eeb6 Binary files /dev/null and b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..27f8dcd Binary files /dev/null and b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..f34ea95 Binary files /dev/null and b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..5007b56 Binary files /dev/null and b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..bfcc770 Binary files /dev/null and b/CHALLENGE_2/sleepysound/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..5f349f7 --- /dev/null +++ b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4..3e80a2d 100644 Binary files a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b7..6bbfdac 100644 Binary files a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 09d4391..aa53945 100644 Binary files a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d..421186e 100644 Binary files a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372e..f087383 100644 Binary files a/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/CHALLENGE_2/sleepysound/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/CHALLENGE_2/sleepysound/android/app/src/main/res/values/colors.xml b/CHALLENGE_2/sleepysound/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..3b44edc --- /dev/null +++ b/CHALLENGE_2/sleepysound/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #121212 + \ No newline at end of file diff --git a/CHALLENGE_2/sleepysound/assets/icons/app_icon.png b/CHALLENGE_2/sleepysound/assets/icons/app_icon.png new file mode 100644 index 0000000..1fd3697 Binary files /dev/null and b/CHALLENGE_2/sleepysound/assets/icons/app_icon.png differ diff --git a/CHALLENGE_2/sleepysound/assets/icons/app_icon_foreground.png b/CHALLENGE_2/sleepysound/assets/icons/app_icon_foreground.png new file mode 100644 index 0000000..c8713ac Binary files /dev/null and b/CHALLENGE_2/sleepysound/assets/icons/app_icon_foreground.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner.xcodeproj/project.pbxproj b/CHALLENGE_2/sleepysound/ios/Runner.xcodeproj/project.pbxproj index f9b1ec5..bfa417c 100644 --- a/CHALLENGE_2/sleepysound/ios/Runner.xcodeproj/project.pbxproj +++ b/CHALLENGE_2/sleepysound/ios/Runner.xcodeproj/project.pbxproj @@ -427,7 +427,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -484,7 +484,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index dc9ada4..b0c09f7 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 7353c41..3a08db0 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 797d452..5cbcf61 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 6ed2d93..1e78f97 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 4cd7b00..37c653f 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index fe73094..3875cf2 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 321773c..4dd58ad 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 797d452..5cbcf61 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index 502f463..a2269a5 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index 0ec3034..082a070 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png new file mode 100644 index 0000000..a729965 Binary files /dev/null and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png new file mode 100644 index 0000000..487e67d Binary files /dev/null and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 0000000..0db5bbf Binary files /dev/null and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png new file mode 100644 index 0000000..3ad426d Binary files /dev/null and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index 0ec3034..082a070 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index e9f5fea..86f6399 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 0000000..3e80a2d Binary files /dev/null and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png new file mode 100644 index 0000000..421186e Binary files /dev/null and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index 84ac32a..c8db051 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 8953cba..fd632fe 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index 0467bf1..837bbac 100644 Binary files a/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/CHALLENGE_2/sleepysound/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/CHALLENGE_2/sleepysound/lib/main.dart b/CHALLENGE_2/sleepysound/lib/main.dart index 0ba6ad7..117b8e6 100644 --- a/CHALLENGE_2/sleepysound/lib/main.dart +++ b/CHALLENGE_2/sleepysound/lib/main.dart @@ -26,7 +26,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (context) => AudioService()), ], child: MaterialApp( - title: 'SleepySound', + title: 'LidoSound', theme: ThemeData( useMaterial3: true, brightness: Brightness.dark, diff --git a/CHALLENGE_2/sleepysound/lib/pages/group_page.dart b/CHALLENGE_2/sleepysound/lib/pages/group_page.dart index 36dd259..765df62 100644 --- a/CHALLENGE_2/sleepysound/lib/pages/group_page.dart +++ b/CHALLENGE_2/sleepysound/lib/pages/group_page.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../services/network_group_service.dart'; -import '../widgets/network_demo_widget.dart'; class GroupPage extends StatefulWidget { const GroupPage({super.key}); @@ -172,9 +171,6 @@ class _GroupPageState extends State { const SizedBox(height: 25), - // Demo Widget - NetworkDemoWidget(networkService: networkService), - // Network Users Section Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -330,13 +326,34 @@ class _GroupPageState extends State { const SizedBox(height: 4), Row( children: [ - Text( - 'Joined ${_formatDuration(DateTime.now().difference(user.joinedAt))} ago', - style: const TextStyle( - color: Colors.grey, - fontSize: 12, + if (user.isListening && user.currentTrackName != null) ...[ + const Icon( + Icons.music_note, + color: Color(0xFF6366F1), + size: 14, ), - ), + const SizedBox(width: 4), + Expanded( + child: Text( + 'Listening to "${user.currentTrackName}" by ${user.currentArtist}', + style: const TextStyle( + color: Color(0xFF6366F1), + fontSize: 12, + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ] else ...[ + Text( + 'Joined ${_formatDuration(DateTime.now().difference(user.joinedAt))} ago', + style: const TextStyle( + color: Colors.grey, + fontSize: 12, + ), + ), + ], if (!user.isOnline) ...[ const Text( ' • ', @@ -366,20 +383,54 @@ class _GroupPageState extends State { ], ), ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: const Color(0xFF6366F1).withOpacity(0.2), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - '${user.votes} votes', - style: const TextStyle( - color: Color(0xFF6366F1), - fontSize: 11, - fontWeight: FontWeight.bold, + Column( + children: [ + if (user.isListening && !isCurrentUser) ...[ + // Join Listening Session Button + IconButton( + onPressed: () async { + final success = await networkService.joinListeningSession(user); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + success + ? 'Joined ${user.name}\'s listening session! 🎵' + : 'Failed to join listening session', + ), + backgroundColor: success + ? const Color(0xFF22C55E) + : const Color(0xFFEF4444), + duration: const Duration(seconds: 3), + ), + ); + } + }, + icon: const Icon( + Icons.headphones, + color: Color(0xFF6366F1), + size: 20, + ), + tooltip: 'Join listening session', + ), + const SizedBox(height: 4), + ], + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xFF6366F1).withOpacity(0.2), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + '${user.votes} votes', + style: const TextStyle( + color: Color(0xFF6366F1), + fontSize: 11, + fontWeight: FontWeight.bold, + ), + ), ), - ), + ], ), ], ), diff --git a/CHALLENGE_2/sleepysound/lib/pages/voting_page.dart b/CHALLENGE_2/sleepysound/lib/pages/voting_page.dart index bbcbcc8..647e9fb 100644 --- a/CHALLENGE_2/sleepysound/lib/pages/voting_page.dart +++ b/CHALLENGE_2/sleepysound/lib/pages/voting_page.dart @@ -15,14 +15,188 @@ class VotingPage extends StatefulWidget { class _VotingPageState extends State { final TextEditingController _searchController = TextEditingController(); + final FocusNode _searchFocusNode = FocusNode(); List _searchResults = []; bool _isLoading = false; String _statusMessage = ''; + + final LayerLink _layerLink = LayerLink(); + OverlayEntry? _overlayEntry; @override void initState() { super.initState(); _loadInitialQueue(); + _searchFocusNode.addListener(_onSearchFocusChange); + } + + @override + void dispose() { + _hideSearchOverlay(); + _searchController.dispose(); + _searchFocusNode.dispose(); + super.dispose(); + } + + void _onSearchFocusChange() { + if (_searchFocusNode.hasFocus && _searchResults.isNotEmpty) { + _showSearchOverlay(); + } else if (!_searchFocusNode.hasFocus) { + // Delay hiding to allow for taps on results + Future.delayed(const Duration(milliseconds: 150), () { + _hideSearchOverlay(); + }); + } + } + + void _showSearchOverlay() { + if (_overlayEntry != null) return; + + _overlayEntry = _createOverlayEntry(); + Overlay.of(context).insert(_overlayEntry!); + } + + void _hideSearchOverlay() { + _overlayEntry?.remove(); + _overlayEntry = null; + } + + OverlayEntry _createOverlayEntry() { + RenderBox renderBox = context.findRenderObject() as RenderBox; + var size = renderBox.size; + var offset = renderBox.localToGlobal(Offset.zero); + + return OverlayEntry( + builder: (context) => Positioned( + left: offset.dx + 20, + top: offset.dy + 200, // Adjust based on search field position + width: size.width - 40, + child: CompositedTransformFollower( + link: _layerLink, + showWhenUnlinked: false, + child: Material( + elevation: 8, + borderRadius: BorderRadius.circular(12), + color: const Color(0xFF1E1E1E), + child: Container( + constraints: const BoxConstraints(maxHeight: 300), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFF6366F1).withOpacity(0.3)), + ), + child: _buildSearchResultsOverlay(), + ), + ), + ), + ), + ); + } + + Widget _buildSearchResultsOverlay() { + if (_searchResults.isEmpty) { + return Container( + padding: const EdgeInsets.all(20), + child: const Text( + 'No results found', + style: TextStyle(color: Colors.grey), + textAlign: TextAlign.center, + ), + ); + } + + return ListView.builder( + shrinkWrap: true, + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: _searchResults.length, + itemBuilder: (context, index) { + final track = _searchResults[index]; + return _buildSearchResultItem(track, index); + }, + ); + } + + Widget _buildSearchResultItem(SpotifyTrack track, int index) { + return InkWell( + onTap: () { + _addToQueue(track); + _hideSearchOverlay(); + _searchController.clear(); + _searchFocusNode.unfocus(); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + // Album Art + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: const Color(0xFF2A2A2A), + ), + child: track.album.images.isNotEmpty + ? ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.network( + track.album.images.first.url, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return const Icon( + Icons.music_note, + color: Colors.grey, + size: 16, + ); + }, + ), + ) + : const Icon( + Icons.music_note, + color: Colors.grey, + size: 16, + ), + ), + const SizedBox(width: 12), + + // Track Info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + track.name, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w500, + fontSize: 14, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + track.artists.map((a) => a.name).join(', '), + style: const TextStyle( + color: Colors.grey, + fontSize: 12, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + + // Add Icon + const Icon( + Icons.add_circle_outline, + color: Color(0xFF6366F1), + size: 20, + ), + ], + ), + ), + ); } Future _loadInitialQueue() async { @@ -31,7 +205,14 @@ class _VotingPageState extends State { } Future _searchSpotify(String query) async { - if (query.isEmpty) return; + if (query.isEmpty) { + setState(() { + _searchResults = []; + _statusMessage = ''; + }); + _hideSearchOverlay(); + return; + } // Check if search query is appropriate if (!GenreFilterService.isSearchQueryAppropriate(query)) { @@ -41,6 +222,7 @@ class _VotingPageState extends State { _statusMessage = 'Search term not suitable for the peaceful Lido atmosphere. Try: ${suggestions.join(', ')}'; _searchResults = []; }); + _hideSearchOverlay(); return; } @@ -57,6 +239,7 @@ class _VotingPageState extends State { _statusMessage = blockMessage ?? 'Please wait $cooldown seconds before searching again.'; _searchResults = []; }); + _hideSearchOverlay(); return; } @@ -69,31 +252,35 @@ class _VotingPageState extends State { final queueService = Provider.of(context, listen: false); final results = await queueService.searchTracks(query); - // Filter results based on genre appropriateness - final filteredResults = GenreFilterService.filterSearchResults(results); + // No filtering on search results - let users see all tracks + // Filtering only happens when adding to queue to maintain atmosphere // Record the suggestion attempt spamService.recordSuggestion(userId); setState(() { - _searchResults = filteredResults; + _searchResults = results; _isLoading = false; - if (filteredResults.isEmpty && results.isNotEmpty) { - _statusMessage = 'No tracks found that match the peaceful Lido atmosphere. Try searching for chill, ambient, or relaxing music.'; - } else if (filteredResults.isEmpty) { + if (results.isEmpty) { _statusMessage = 'No tracks found for "$query"'; } else { - final filtered = results.length - filteredResults.length; - _statusMessage = 'Found ${filteredResults.length} tracks' + - (filtered > 0 ? ' ($filtered filtered for atmosphere)' : ''); + _statusMessage = 'Found ${results.length} tracks'; } }); + + // Show overlay if we have results and search field is focused + if (results.isNotEmpty && _searchFocusNode.hasFocus) { + _showSearchOverlay(); + } else { + _hideSearchOverlay(); + } } catch (e) { setState(() { _isLoading = false; _statusMessage = 'Search failed: ${e.toString()}'; _searchResults = []; }); + _hideSearchOverlay(); } } @@ -202,34 +389,74 @@ class _VotingPageState extends State { const SizedBox(height: 20), // Search Bar - TextField( - controller: _searchController, - style: const TextStyle(color: Colors.white), - decoration: InputDecoration( - hintText: 'Search for songs, artists, albums...', - hintStyle: const TextStyle(color: Colors.grey), - prefixIcon: const Icon(Icons.search, color: Colors.grey), - suffixIcon: _isLoading - ? const Padding( - padding: EdgeInsets.all(12), - child: SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Color(0xFF6366F1)), + CompositedTransformTarget( + link: _layerLink, + child: TextField( + controller: _searchController, + focusNode: _searchFocusNode, + style: const TextStyle(color: Colors.white), + decoration: InputDecoration( + hintText: 'Search for songs, artists, albums...', + hintStyle: const TextStyle(color: Colors.grey), + prefixIcon: const Icon(Icons.search, color: Colors.grey), + suffixIcon: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (_isLoading) + const Padding( + padding: EdgeInsets.all(12), + child: SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Color(0xFF6366F1)), + ), ), ), - ) - : null, - filled: true, - fillColor: const Color(0xFF1E1E1E), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, + if (_searchController.text.isNotEmpty) + IconButton( + icon: const Icon(Icons.clear, color: Colors.grey), + onPressed: () { + _searchController.clear(); + _hideSearchOverlay(); + setState(() { + _searchResults = []; + _statusMessage = ''; + }); + }, + ), + ], + ), + filled: true, + fillColor: const Color(0xFF1E1E1E), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: Color(0xFF6366F1), width: 2), + ), ), + onChanged: (value) { + // Search as user types (with debounce) + if (value.length >= 3) { + Future.delayed(const Duration(milliseconds: 500), () { + if (_searchController.text == value) { + _searchSpotify(value); + } + }); + } else if (value.isEmpty) { + setState(() { + _searchResults = []; + _statusMessage = ''; + }); + _hideSearchOverlay(); + } + }, + onSubmitted: _searchSpotify, ), - onSubmitted: _searchSpotify, ), // Atmosphere Info @@ -323,33 +550,51 @@ class _VotingPageState extends State { ), ), - // Search Results and Queue + // Queue Section Expanded( - child: DefaultTabController( - length: 2, - child: Column( - children: [ - const TabBar( - labelColor: Color(0xFF6366F1), - unselectedLabelColor: Colors.grey, - indicatorColor: Color(0xFF6366F1), - tabs: [ - Tab(text: 'Search Results'), - Tab(text: 'Queue'), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Music Queue', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: const Color(0xFF6366F1).withOpacity(0.2), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: const Color(0xFF6366F1), + width: 1, + ), + ), + child: Text( + '${queueService.queue.length} songs', + style: const TextStyle( + color: Color(0xFF6366F1), + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ), ], ), - Expanded( - child: TabBarView( - children: [ - // Search Results Tab - _buildSearchResults(), - // Queue Tab - _buildQueueView(queueService), - ], - ), - ), - ], - ), + ), + const SizedBox(height: 16), + Expanded( + child: _buildQueueView(queueService), + ), + ], ), ), ], @@ -360,40 +605,6 @@ class _VotingPageState extends State { ); } - Widget _buildSearchResults() { - if (_searchResults.isEmpty && !_isLoading) { - return const Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.search, - size: 80, - color: Colors.grey, - ), - SizedBox(height: 20), - Text( - 'Search for songs to add to the queue', - style: TextStyle( - color: Colors.grey, - fontSize: 16, - ), - ), - ], - ), - ); - } - - return ListView.builder( - padding: const EdgeInsets.all(20), - itemCount: _searchResults.length, - itemBuilder: (context, index) { - final track = _searchResults[index]; - return _buildTrackCard(track); - }, - ); - } - Widget _buildQueueView(MusicQueueService queueService) { final queue = queueService.queue; @@ -439,97 +650,6 @@ class _VotingPageState extends State { ); } - Widget _buildTrackCard(SpotifyTrack track) { - return Card( - color: const Color(0xFF1E1E1E), - margin: const EdgeInsets.only(bottom: 12), - child: Padding( - padding: const EdgeInsets.all(12), - child: Row( - children: [ - // Album Art - Container( - width: 60, - height: 60, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - color: const Color(0xFF2A2A2A), - ), - child: track.album.images.isNotEmpty - ? ClipRRect( - borderRadius: BorderRadius.circular(8), - child: Image.network( - track.album.images.first.url, - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return const Icon( - Icons.music_note, - color: Colors.grey, - ); - }, - ), - ) - : const Icon( - Icons.music_note, - color: Colors.grey, - ), - ), - const SizedBox(width: 12), - - // Track Info - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - track.name, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 4), - Text( - track.artists.map((a) => a.name).join(', '), - style: const TextStyle( - color: Colors.grey, - fontSize: 14, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 4), - Text( - track.album.name, - style: const TextStyle( - color: Colors.grey, - fontSize: 12, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - - // Add Button - IconButton( - onPressed: () => _addToQueue(track), - icon: const Icon( - Icons.add_circle, - color: Color(0xFF6366F1), - size: 32, - ), - ), - ], - ), - ), - ); - } - Widget _buildQueueItemCard(QueueItem queueItem, int index, MusicQueueService queueService) { return Card( color: const Color(0xFF1E1E1E), diff --git a/CHALLENGE_2/sleepysound/lib/services/network_group_service.dart b/CHALLENGE_2/sleepysound/lib/services/network_group_service.dart index c706210..ccdca0f 100644 --- a/CHALLENGE_2/sleepysound/lib/services/network_group_service.dart +++ b/CHALLENGE_2/sleepysound/lib/services/network_group_service.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:network_info_plus/network_info_plus.dart'; import 'package:multicast_dns/multicast_dns.dart'; +import 'music_queue_service.dart'; class NetworkUser { final String id; @@ -15,6 +16,11 @@ class NetworkUser { final int votes; bool isOnline; DateTime lastSeen; + String? currentTrackId; + String? currentTrackName; + String? currentArtist; + String? currentTrackImage; + bool isListening; NetworkUser({ required this.id, @@ -24,6 +30,11 @@ class NetworkUser { this.votes = 0, this.isOnline = true, DateTime? lastSeen, + this.currentTrackId, + this.currentTrackName, + this.currentArtist, + this.currentTrackImage, + this.isListening = false, }) : lastSeen = lastSeen ?? DateTime.now(); Map toJson() => { @@ -34,6 +45,11 @@ class NetworkUser { 'votes': votes, 'isOnline': isOnline, 'lastSeen': lastSeen.toIso8601String(), + 'currentTrackId': currentTrackId, + 'currentTrackName': currentTrackName, + 'currentArtist': currentArtist, + 'currentTrackImage': currentTrackImage, + 'isListening': isListening, }; factory NetworkUser.fromJson(Map json) => NetworkUser( @@ -44,6 +60,11 @@ class NetworkUser { votes: json['votes'] ?? 0, isOnline: json['isOnline'] ?? true, lastSeen: DateTime.parse(json['lastSeen']), + currentTrackId: json['currentTrackId'], + currentTrackName: json['currentTrackName'], + currentArtist: json['currentArtist'], + currentTrackImage: json['currentTrackImage'], + isListening: json['isListening'] ?? false, ); } @@ -66,6 +87,7 @@ class NetworkGroupService extends ChangeNotifier { final Map _networkUsers = {}; late NetworkUser _currentUser; + MusicQueueService? _musicService; // Getters bool get isConnectedToWifi => _isConnectedToWifi; @@ -79,6 +101,8 @@ class NetworkGroupService extends ChangeNotifier { NetworkGroupService() { _initializeCurrentUser(); _startNetworkMonitoring(); + // Initialize music service reference + _musicService = MusicQueueService(); } void _initializeCurrentUser() { @@ -232,6 +256,8 @@ class NetworkGroupService extends ChangeNotifier { response.headers.set('Access-Control-Allow-Origin', '*'); if (request.method == 'GET' && request.uri.path == '/user') { + // Update current user with latest listening info before sending + await _updateCurrentUserListeningInfo(); // Return current user info response.write(jsonEncode(_currentUser.toJson())); } else if (request.method == 'POST' && request.uri.path == '/heartbeat') { @@ -244,6 +270,41 @@ class NetworkGroupService extends ChangeNotifier { notifyListeners(); response.write(jsonEncode({'status': 'ok'})); + } else if (request.method == 'POST' && request.uri.path == '/join-session') { + // Handle request to join this user's listening session + final body = await utf8.decoder.bind(request).join(); + // Parse request data for future use (logging, analytics, etc.) + jsonDecode(body); + + // Get current track info to send back + final currentTrackInfo = _musicService?.currentTrackInfo; + if (currentTrackInfo != null) { + response.write(jsonEncode({ + 'status': 'ok', + 'trackInfo': currentTrackInfo, + 'message': 'Successfully joined listening session' + })); + } else { + response.write(jsonEncode({ + 'status': 'no_track', + 'message': 'No track currently playing' + })); + } + } else if (request.method == 'GET' && request.uri.path == '/current-track') { + // Get current track info without joining + await _updateCurrentUserListeningInfo(); + final currentTrackInfo = _musicService?.currentTrackInfo; + if (currentTrackInfo != null) { + response.write(jsonEncode({ + 'status': 'ok', + 'trackInfo': currentTrackInfo + })); + } else { + response.write(jsonEncode({ + 'status': 'no_track', + 'message': 'No track currently playing' + })); + } } else { response.statusCode = 404; response.write(jsonEncode({'error': 'Not found'})); @@ -395,6 +456,39 @@ class NetworkGroupService extends ChangeNotifier { } } + Future _updateCurrentUserListeningInfo() async { + final currentTrackInfo = _musicService?.currentTrackInfo; + final currentTrack = _musicService?.currentTrack; + + if (currentTrack != null && currentTrackInfo != null) { + _currentUser = NetworkUser( + id: _currentUser.id, + name: _currentUser.name, + ipAddress: _currentUser.ipAddress, + joinedAt: _currentUser.joinedAt, + votes: _currentUser.votes, + isOnline: true, + currentTrackId: currentTrack.id, + currentTrackName: currentTrack.name, + currentArtist: currentTrack.artistNames, + currentTrackImage: currentTrack.imageUrl, + isListening: currentTrackInfo['isPlaying'] ?? false, + ); + } else { + _currentUser = NetworkUser( + id: _currentUser.id, + name: _currentUser.name, + ipAddress: _currentUser.ipAddress, + joinedAt: _currentUser.joinedAt, + votes: _currentUser.votes, + isOnline: true, + isListening: false, + ); + } + + _networkUsers[_currentUser.id] = _currentUser; + } + // Public methods for UI interaction Future refreshNetwork() async { await _checkConnectivity(); @@ -426,6 +520,45 @@ class NetworkGroupService extends ChangeNotifier { return 'Connected to $_currentNetworkName'; } + // Join another user's listening session + Future joinListeningSession(NetworkUser user) async { + if (!user.isListening || user.currentTrackId == null) { + return false; + } + + try { + final client = HttpClient(); + client.connectionTimeout = const Duration(seconds: 5); + + final request = await client.postUrl( + Uri.parse('http://${user.ipAddress}:$_discoveryPort/join-session') + ); + request.headers.set('Content-Type', 'application/json'); + request.write(jsonEncode({'userId': _currentUser.id})); + + final response = await request.close().timeout(const Duration(seconds: 5)); + + if (response.statusCode == 200) { + final body = await utf8.decoder.bind(response).join(); + final responseData = jsonDecode(body); + + if (responseData['status'] == 'ok' && responseData['trackInfo'] != null) { + // Here you could sync the track with your local player + // For now, we'll just return success + return true; + } + } + + client.close(); + return false; + } catch (e) { + if (kDebugMode) { + print('Error joining listening session: $e'); + } + return false; + } + } + // Demo methods for testing void simulateNetworkConnection() { _isConnectedToWifi = true; diff --git a/CHALLENGE_2/sleepysound/lib/services/spam_protection_service.dart b/CHALLENGE_2/sleepysound/lib/services/spam_protection_service.dart index d6472a8..3eaeb49 100644 --- a/CHALLENGE_2/sleepysound/lib/services/spam_protection_service.dart +++ b/CHALLENGE_2/sleepysound/lib/services/spam_protection_service.dart @@ -34,7 +34,13 @@ class SpamProtectionService extends ChangeNotifier { return false; } - // Check cooldown + // Allow first vote without cooldown for smooth user experience + final votes = _userVotes[userId] ?? []; + if (votes.isEmpty) { + return true; // First vote is always allowed + } + + // Check cooldown for subsequent votes if (_isOnCooldown(userId, _lastVoteTime, voteCooldown)) { return false; } @@ -54,7 +60,13 @@ class SpamProtectionService extends ChangeNotifier { return false; } - // Check cooldown + // Allow first suggestion without cooldown for smooth user experience + final suggestions = _userSuggestions[userId] ?? []; + if (suggestions.isEmpty) { + return true; // First suggestion is always allowed + } + + // Check cooldown for subsequent suggestions if (_isOnCooldown(userId, _lastSuggestionTime, suggestionCooldown)) { return false; } diff --git a/CHALLENGE_2/sleepysound/pubspec.lock b/CHALLENGE_2/sleepysound/pubspec.lock index 3cce59a..952bb96 100644 --- a/CHALLENGE_2/sleepysound/pubspec.lock +++ b/CHALLENGE_2/sleepysound/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.7.1" + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" args: dependency: transitive description: @@ -177,6 +185,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" clock: dependency: transitive description: @@ -294,6 +310,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" + source: hosted + version: "0.13.1" flutter_lints: dependency: "direct dev" description: @@ -360,6 +384,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + image: + dependency: transitive + description: + name: image + sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" + url: "https://pub.dev" + source: hosted + version: "4.5.4" io: dependency: transitive description: @@ -600,6 +632,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" provider: dependency: "direct main" description: diff --git a/CHALLENGE_2/sleepysound/pubspec.yaml b/CHALLENGE_2/sleepysound/pubspec.yaml index d56c97c..fef8b3e 100644 --- a/CHALLENGE_2/sleepysound/pubspec.yaml +++ b/CHALLENGE_2/sleepysound/pubspec.yaml @@ -77,6 +77,9 @@ dev_dependencies: # JSON serialization json_serializable: ^6.7.1 build_runner: ^2.4.7 + + # App icon generator + flutter_launcher_icons: ^0.13.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -92,6 +95,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - assets/audio/ + - assets/icons/ # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg @@ -120,3 +124,13 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/to/font-from-package + +flutter_launcher_icons: + android: true + ios: true + web: + generate: true + image_path: "assets/icons/app_icon.png" + adaptive_icon_background: "#121212" + adaptive_icon_foreground: "assets/icons/app_icon_foreground.png" + remove_alpha_ios: true diff --git a/CHALLENGE_2/sleepysound/web/favicon.png b/CHALLENGE_2/sleepysound/web/favicon.png index 8aaa46a..4cf62ff 100644 Binary files a/CHALLENGE_2/sleepysound/web/favicon.png and b/CHALLENGE_2/sleepysound/web/favicon.png differ diff --git a/CHALLENGE_2/sleepysound/web/icons/Icon-192.png b/CHALLENGE_2/sleepysound/web/icons/Icon-192.png index b749bfe..f087383 100644 Binary files a/CHALLENGE_2/sleepysound/web/icons/Icon-192.png and b/CHALLENGE_2/sleepysound/web/icons/Icon-192.png differ diff --git a/CHALLENGE_2/sleepysound/web/icons/Icon-512.png b/CHALLENGE_2/sleepysound/web/icons/Icon-512.png index 88cfd48..03214a1 100644 Binary files a/CHALLENGE_2/sleepysound/web/icons/Icon-512.png and b/CHALLENGE_2/sleepysound/web/icons/Icon-512.png differ diff --git a/CHALLENGE_2/sleepysound/web/icons/Icon-maskable-192.png b/CHALLENGE_2/sleepysound/web/icons/Icon-maskable-192.png index eb9b4d7..f087383 100644 Binary files a/CHALLENGE_2/sleepysound/web/icons/Icon-maskable-192.png and b/CHALLENGE_2/sleepysound/web/icons/Icon-maskable-192.png differ diff --git a/CHALLENGE_2/sleepysound/web/icons/Icon-maskable-512.png b/CHALLENGE_2/sleepysound/web/icons/Icon-maskable-512.png index d69c566..03214a1 100644 Binary files a/CHALLENGE_2/sleepysound/web/icons/Icon-maskable-512.png and b/CHALLENGE_2/sleepysound/web/icons/Icon-maskable-512.png differ diff --git a/CHALLENGE_2/sleepysound/web/manifest.json b/CHALLENGE_2/sleepysound/web/manifest.json index 59317b1..e9218d8 100644 --- a/CHALLENGE_2/sleepysound/web/manifest.json +++ b/CHALLENGE_2/sleepysound/web/manifest.json @@ -32,4 +32,4 @@ "purpose": "maskable" } ] -} +} \ No newline at end of file