♻️ Moved player state from shared_prefs to drift
🍱 Update icons
			
			
@@ -1,10 +1,14 @@
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
 | 
			
		||||
    <uses-permission android:name="android.permission.WAKE_LOCK" />
 | 
			
		||||
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:label="rhythm_box"
 | 
			
		||||
        android:label="RhythmBox"
 | 
			
		||||
        android:name="${applicationName}"
 | 
			
		||||
        android:icon="@mipmap/ic_launcher">
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".MainActivity"
 | 
			
		||||
            android:name="com.ryanheise.audioservice.AudioServiceActivity"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:launchMode="singleTop"
 | 
			
		||||
            android:taskAffinity=""
 | 
			
		||||
@@ -17,14 +21,30 @@
 | 
			
		||||
                 while the Flutter UI initializes. After that, this theme continues
 | 
			
		||||
                 to determine the Window background behind the Flutter UI. -->
 | 
			
		||||
            <meta-data
 | 
			
		||||
              android:name="io.flutter.embedding.android.NormalTheme"
 | 
			
		||||
              android:resource="@style/NormalTheme"
 | 
			
		||||
              />
 | 
			
		||||
                android:name="io.flutter.embedding.android.NormalTheme"
 | 
			
		||||
                android:resource="@style/NormalTheme"
 | 
			
		||||
            />
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.MAIN"/>
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER"/>
 | 
			
		||||
                <action android:name="android.intent.action.MAIN" />
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <service android:name="com.ryanheise.audioservice.AudioService"
 | 
			
		||||
            android:foregroundServiceType="mediaPlayback"
 | 
			
		||||
            android:exported="true" tools:ignore="Instantiatable">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.media.browse.MediaBrowserService" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </service>
 | 
			
		||||
 | 
			
		||||
        <receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver"
 | 
			
		||||
            android:exported="true" tools:ignore="Instantiatable">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.MEDIA_BUTTON" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </receiver>
 | 
			
		||||
 | 
			
		||||
        <!-- Don't delete the meta-data below.
 | 
			
		||||
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
 | 
			
		||||
        <meta-data
 | 
			
		||||
@@ -38,8 +58,8 @@
 | 
			
		||||
         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
 | 
			
		||||
    <queries>
 | 
			
		||||
        <intent>
 | 
			
		||||
            <action android:name="android.intent.action.PROCESS_TEXT"/>
 | 
			
		||||
            <data android:mimeType="text/plain"/>
 | 
			
		||||
            <action android:name="android.intent.action.PROCESS_TEXT" />
 | 
			
		||||
            <data android:mimeType="text/plain" />
 | 
			
		||||
        </intent>
 | 
			
		||||
    </queries>
 | 
			
		||||
</manifest>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/ic_launcher-playstore.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 8.4 KiB  | 
							
								
								
									
										15
									
								
								android/app/src/main/res/drawable-anydpi/ic_stat_name.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:tint="#FFFFFF">
 | 
			
		||||
  <group android:scaleX="1.127451"
 | 
			
		||||
      android:scaleY="1.127451"
 | 
			
		||||
      android:translateX="-1.5294118"
 | 
			
		||||
      android:translateY="-1.5294118">
 | 
			
		||||
    <path
 | 
			
		||||
        android:pathData="M16,9H13V14.5A2.5,2.5 0,0 1,10.5 17A2.5,2.5 0,0 1,8 14.5A2.5,2.5 0,0 1,10.5 12C11.07,12 11.58,12.19 12,12.5V7H16M19,3H5A2,2 0,0 0,3 5V19A2,2 0,0 0,5 21H19A2,2 0,0 0,21 19V5A2,2 0,0 0,19 3Z"
 | 
			
		||||
        android:fillColor="#fff"/>
 | 
			
		||||
  </group>
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/drawable-hdpi/ic_stat_name.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 337 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/drawable-mdpi/ic_stat_name.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 240 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/drawable-xhdpi/ic_stat_name.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 461 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/drawable-xxhdpi/ic_stat_name.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 656 B  | 
							
								
								
									
										74
									
								
								android/app/src/main/res/drawable/ic_launcher_background.xml
									
									
									
									
									
										Normal 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>
 | 
			
		||||
							
								
								
									
										14
									
								
								android/app/src/main/res/drawable/ic_launcher_foreground.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,14 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="108dp"
 | 
			
		||||
    android:height="108dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24">
 | 
			
		||||
  <group android:scaleX="0.88536584"
 | 
			
		||||
      android:scaleY="0.88536584"
 | 
			
		||||
      android:translateX="1.3756098"
 | 
			
		||||
      android:translateY="1.3756098">
 | 
			
		||||
    <path
 | 
			
		||||
        android:pathData="M16,9H13V14.5A2.5,2.5 0,0 1,10.5 17A2.5,2.5 0,0 1,8 14.5A2.5,2.5 0,0 1,10.5 12C11.07,12 11.58,12.19 12,12.5V7H16M19,3H5A2,2 0,0 0,3 5V19A2,2 0,0 0,5 21H19A2,2 0,0 0,21 19V5A2,2 0,0 0,19 3Z"
 | 
			
		||||
        android:fillColor="#fff"/>
 | 
			
		||||
  </group>
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -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_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
@@ -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_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 544 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-hdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 774 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-hdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.6 KiB  | 
| 
		 Before Width: | Height: | Size: 442 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-mdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 672 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 888 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-mdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 985 B  | 
| 
		 Before Width: | Height: | Size: 721 B  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 After Width: | Height: | Size: 1.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.3 KiB  | 
| 
		 Before Width: | Height: | Size: 1.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 After Width: | Height: | Size: 3.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 1.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.4 KiB  | 
| 
		 After Width: | Height: | Size: 4.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.5 KiB  | 
@@ -0,0 +1,4 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <color name="ic_launcher_background">#BD7F44</color>
 | 
			
		||||
</resources>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								assets/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 62 KiB  | 
@@ -540,7 +540,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++";
 | 
			
		||||
@@ -597,7 +597,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++";
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 47 KiB  | 
| 
		 Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 395 B  | 
| 
		 Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 788 B  | 
| 
		 Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 588 B  | 
| 
		 Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 2.0 KiB  | 
| 
		 Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 788 B  | 
| 
		 Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 1.8 KiB  | 
| 
		 Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 3.0 KiB  | 
| 
		 After Width: | Height: | Size: 1009 B  | 
| 
		 After Width: | Height: | Size: 2.4 KiB  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 3.0 KiB  | 
| 
		 Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 5.0 KiB  | 
| 
		 After Width: | Height: | Size: 1.6 KiB  | 
| 
		 After Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 1.7 KiB  | 
| 
		 Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 4.0 KiB  | 
| 
		 Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.6 KiB  | 
@@ -1,19 +1,16 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'dart:convert';
 | 
			
		||||
import 'dart:math';
 | 
			
		||||
 | 
			
		||||
import 'package:get/get.dart';
 | 
			
		||||
import 'package:drift/drift.dart';
 | 
			
		||||
import 'package:get/get.dart' hide Value;
 | 
			
		||||
import 'package:media_kit/media_kit.dart' hide Track;
 | 
			
		||||
import 'package:rhythm_box/providers/database.dart';
 | 
			
		||||
import 'package:rhythm_box/services/audio_player/state.dart';
 | 
			
		||||
import 'package:rhythm_box/services/local_track.dart';
 | 
			
		||||
import 'package:rhythm_box/services/server/sourced_track.dart';
 | 
			
		||||
import 'package:rhythm_box/services/database/database.dart';
 | 
			
		||||
import 'package:spotify/spotify.dart' hide Playlist;
 | 
			
		||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
 | 
			
		||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
			
		||||
 | 
			
		||||
class AudioPlayerProvider extends GetxController {
 | 
			
		||||
  late final SharedPreferences _prefs;
 | 
			
		||||
 | 
			
		||||
  RxBool isPlaying = false.obs;
 | 
			
		||||
 | 
			
		||||
  Rx<AudioPlayerState> state = Rx(AudioPlayerState(
 | 
			
		||||
@@ -28,41 +25,39 @@ class AudioPlayerProvider extends GetxController {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void onInit() {
 | 
			
		||||
    SharedPreferences.getInstance().then((ins) async {
 | 
			
		||||
      _prefs = ins;
 | 
			
		||||
      final res = await _readSavedState();
 | 
			
		||||
      if (res != null) {
 | 
			
		||||
        state.value = res;
 | 
			
		||||
      } else {
 | 
			
		||||
        state.value = AudioPlayerState(
 | 
			
		||||
          loopMode: audioPlayer.loopMode,
 | 
			
		||||
          playing: audioPlayer.isPlaying,
 | 
			
		||||
          playlist: audioPlayer.playlist,
 | 
			
		||||
          shuffled: audioPlayer.isShuffled,
 | 
			
		||||
          collections: [],
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _subscriptions = [
 | 
			
		||||
      audioPlayer.playingStream.listen((playing) async {
 | 
			
		||||
        state.value = state.value.copyWith(playing: playing);
 | 
			
		||||
        await _updateSavedState();
 | 
			
		||||
        await _updatePlayerState(
 | 
			
		||||
          AudioPlayerStateTableCompanion(
 | 
			
		||||
            playing: Value(playing),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }),
 | 
			
		||||
      audioPlayer.loopModeStream.listen((loopMode) async {
 | 
			
		||||
        state.value = state.value.copyWith(loopMode: loopMode);
 | 
			
		||||
        await _updateSavedState();
 | 
			
		||||
        await _updatePlayerState(
 | 
			
		||||
          AudioPlayerStateTableCompanion(
 | 
			
		||||
            loopMode: Value(loopMode),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }),
 | 
			
		||||
      audioPlayer.shuffledStream.listen((shuffled) async {
 | 
			
		||||
        state.value = state.value.copyWith(shuffled: shuffled);
 | 
			
		||||
        await _updateSavedState();
 | 
			
		||||
        await _updatePlayerState(
 | 
			
		||||
          AudioPlayerStateTableCompanion(
 | 
			
		||||
            shuffled: Value(shuffled),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }),
 | 
			
		||||
      audioPlayer.playlistStream.listen((playlist) async {
 | 
			
		||||
        state.value = state.value.copyWith(playlist: playlist);
 | 
			
		||||
        await _updateSavedState();
 | 
			
		||||
        await _updatePlaylist(playlist);
 | 
			
		||||
      }),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    _readSavedState();
 | 
			
		||||
 | 
			
		||||
    audioPlayer.playingStream.listen((playing) {
 | 
			
		||||
      isPlaying.value = playing;
 | 
			
		||||
    });
 | 
			
		||||
@@ -80,16 +75,124 @@ class AudioPlayerProvider extends GetxController {
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<AudioPlayerState?> _readSavedState() async {
 | 
			
		||||
    final data = _prefs.getString('player_state');
 | 
			
		||||
    if (data == null) return null;
 | 
			
		||||
  Future<void> _readSavedState() async {
 | 
			
		||||
    final database = Get.find<DatabaseProvider>().database;
 | 
			
		||||
 | 
			
		||||
    return AudioPlayerState.fromJson(jsonDecode(data));
 | 
			
		||||
    var playerState =
 | 
			
		||||
        await database.select(database.audioPlayerStateTable).getSingleOrNull();
 | 
			
		||||
 | 
			
		||||
    if (playerState == null) {
 | 
			
		||||
      await database.into(database.audioPlayerStateTable).insert(
 | 
			
		||||
            AudioPlayerStateTableCompanion.insert(
 | 
			
		||||
              playing: audioPlayer.isPlaying,
 | 
			
		||||
              loopMode: audioPlayer.loopMode,
 | 
			
		||||
              shuffled: audioPlayer.isShuffled,
 | 
			
		||||
              collections: <String>[],
 | 
			
		||||
              id: const Value(0),
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
      playerState =
 | 
			
		||||
          await database.select(database.audioPlayerStateTable).getSingle();
 | 
			
		||||
    } else {
 | 
			
		||||
      await audioPlayer.setLoopMode(playerState.loopMode);
 | 
			
		||||
      await audioPlayer.setShuffle(playerState.shuffled);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var playlist =
 | 
			
		||||
        await database.select(database.playlistTable).getSingleOrNull();
 | 
			
		||||
    var medias = await database.select(database.playlistMediaTable).get();
 | 
			
		||||
 | 
			
		||||
    if (playlist == null) {
 | 
			
		||||
      await database.into(database.playlistTable).insert(
 | 
			
		||||
            PlaylistTableCompanion.insert(
 | 
			
		||||
              audioPlayerStateId: 0,
 | 
			
		||||
              index: audioPlayer.playlist.index,
 | 
			
		||||
              id: const Value(0),
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
      playlist = await database.select(database.playlistTable).getSingle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (medias.isEmpty && audioPlayer.playlist.medias.isNotEmpty) {
 | 
			
		||||
      await database.batch((batch) {
 | 
			
		||||
        batch.insertAll(
 | 
			
		||||
          database.playlistMediaTable,
 | 
			
		||||
          [
 | 
			
		||||
            for (final media in audioPlayer.playlist.medias)
 | 
			
		||||
              PlaylistMediaTableCompanion.insert(
 | 
			
		||||
                playlistId: playlist!.id,
 | 
			
		||||
                uri: media.uri,
 | 
			
		||||
                extras: Value(media.extras),
 | 
			
		||||
                httpHeaders: Value(media.httpHeaders),
 | 
			
		||||
              ),
 | 
			
		||||
          ],
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
    } else if (medias.isNotEmpty) {
 | 
			
		||||
      await audioPlayer.openPlaylist(
 | 
			
		||||
        medias
 | 
			
		||||
            .map(
 | 
			
		||||
              (media) => RhythmMedia.fromMedia(
 | 
			
		||||
                Media(
 | 
			
		||||
                  media.uri,
 | 
			
		||||
                  extras: media.extras,
 | 
			
		||||
                  httpHeaders: media.httpHeaders,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            )
 | 
			
		||||
            .toList(),
 | 
			
		||||
        initialIndex: playlist.index,
 | 
			
		||||
        autoPlay: false,
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (playerState.collections.isNotEmpty) {
 | 
			
		||||
      state.value = state.value.copyWith(
 | 
			
		||||
        collections: playerState.collections,
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _updateSavedState() async {
 | 
			
		||||
    final out = jsonEncode(state.value.toJson());
 | 
			
		||||
    await _prefs.setString('player_state', out);
 | 
			
		||||
  Future<void> _updatePlayerState(
 | 
			
		||||
    AudioPlayerStateTableCompanion companion,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final database = Get.find<DatabaseProvider>().database;
 | 
			
		||||
 | 
			
		||||
    await (database.update(database.audioPlayerStateTable)
 | 
			
		||||
          ..where((tb) => tb.id.equals(0)))
 | 
			
		||||
        .write(companion);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _updatePlaylist(
 | 
			
		||||
    Playlist playlist,
 | 
			
		||||
  ) async {
 | 
			
		||||
    final database = Get.find<DatabaseProvider>().database;
 | 
			
		||||
 | 
			
		||||
    await database.batch((batch) {
 | 
			
		||||
      batch.update(
 | 
			
		||||
        database.playlistTable,
 | 
			
		||||
        PlaylistTableCompanion(index: Value(playlist.index)),
 | 
			
		||||
        where: (tb) => tb.id.equals(0),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      batch.deleteAll(database.playlistMediaTable);
 | 
			
		||||
 | 
			
		||||
      if (playlist.medias.isEmpty) return;
 | 
			
		||||
      batch.insertAll(
 | 
			
		||||
        database.playlistMediaTable,
 | 
			
		||||
        [
 | 
			
		||||
          for (final media in playlist.medias)
 | 
			
		||||
            PlaylistMediaTableCompanion.insert(
 | 
			
		||||
              playlistId: 0,
 | 
			
		||||
              uri: media.uri,
 | 
			
		||||
              extras: Value(media.extras),
 | 
			
		||||
              httpHeaders: Value(media.httpHeaders),
 | 
			
		||||
            ),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> addCollections(List<String> collectionIds) async {
 | 
			
		||||
@@ -98,7 +201,11 @@ class AudioPlayerProvider extends GetxController {
 | 
			
		||||
      ...collectionIds,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    await _updateSavedState();
 | 
			
		||||
    await _updatePlayerState(
 | 
			
		||||
      AudioPlayerStateTableCompanion(
 | 
			
		||||
        collections: Value(state.value.collections),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> addCollection(String collectionId) async {
 | 
			
		||||
@@ -112,7 +219,11 @@ class AudioPlayerProvider extends GetxController {
 | 
			
		||||
          .toList(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await _updateSavedState();
 | 
			
		||||
    await _updatePlayerState(
 | 
			
		||||
      AudioPlayerStateTableCompanion(
 | 
			
		||||
        collections: Value(state.value.collections),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> removeCollection(String collectionId) async {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,16 @@ class AudioPlayerStreamProvider extends GetxController {
 | 
			
		||||
  List<StreamSubscription>? _subscriptions;
 | 
			
		||||
 | 
			
		||||
  AudioPlayerStreamProvider() {
 | 
			
		||||
    AudioServices.create().then(
 | 
			
		||||
      (value) => notificationService = value,
 | 
			
		||||
    );
 | 
			
		||||
    _initNotificationService();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _initNotificationService() async {
 | 
			
		||||
    try {
 | 
			
		||||
      final res = await AudioServices.create();
 | 
			
		||||
      notificationService = res;
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      log('[AudioService] Unable to init audio service: $err');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@@ -50,7 +57,7 @@ class AudioPlayerStreamProvider extends GetxController {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> updatePalette() async {
 | 
			
		||||
    if (!Get.find<UserPreferences>().albumColorSync) {
 | 
			
		||||
    if (!Get.find<UserPreferencesProvider>().state.value.albumColorSync) {
 | 
			
		||||
      if (palette.value != null) {
 | 
			
		||||
        palette.value = null;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -155,7 +155,10 @@ class _PlayerScreenState extends State<PlayerScreen> {
 | 
			
		||||
                    ),
 | 
			
		||||
                    child: Slider(
 | 
			
		||||
                      value: _draggingValue ??
 | 
			
		||||
                          _durationCurrent.inMilliseconds.toDouble(),
 | 
			
		||||
                          (_durationCurrent.inMilliseconds <=
 | 
			
		||||
                                  _durationTotal.inMilliseconds
 | 
			
		||||
                              ? _durationCurrent.inMilliseconds.toDouble()
 | 
			
		||||
                              : 0),
 | 
			
		||||
                      min: 0,
 | 
			
		||||
                      max: _durationTotal.inMilliseconds.toDouble(),
 | 
			
		||||
                      onChanged: (value) {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ class AudioServices with WidgetsBindingObserver {
 | 
			
		||||
                      : 'dev.solsynth.rhythmBox',
 | 
			
		||||
                  androidNotificationChannelName: 'RhythmBox',
 | 
			
		||||
                  androidNotificationOngoing: false,
 | 
			
		||||
                  androidNotificationIcon: 'drawable/ic_launcher_monochrome',
 | 
			
		||||
                  androidNotificationIcon: 'drawable/ic_stat_name',
 | 
			
		||||
                  androidStopForegroundOnPause: false,
 | 
			
		||||
                  androidNotificationChannelDescription: 'RhythmBox Music',
 | 
			
		||||
                ),
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:drift/drift.dart';
 | 
			
		||||
import 'package:encrypt/encrypt.dart';
 | 
			
		||||
import 'package:media_kit/media_kit.dart' hide Track;
 | 
			
		||||
import 'package:path/path.dart';
 | 
			
		||||
import 'package:path_provider/path_provider.dart';
 | 
			
		||||
import 'package:rhythm_box/services/color.dart';
 | 
			
		||||
@@ -27,6 +28,7 @@ part 'tables/skip_segment.dart';
 | 
			
		||||
part 'tables/source_match.dart';
 | 
			
		||||
part 'tables/history.dart';
 | 
			
		||||
part 'tables/lyrics.dart';
 | 
			
		||||
part 'tables/audio_player_state.dart';
 | 
			
		||||
 | 
			
		||||
part 'typeconverters/color.dart';
 | 
			
		||||
part 'typeconverters/locale.dart';
 | 
			
		||||
@@ -44,6 +46,9 @@ part 'typeconverters/subtitle.dart';
 | 
			
		||||
    SourceMatchTable,
 | 
			
		||||
    HistoryTable,
 | 
			
		||||
    LyricsTable,
 | 
			
		||||
    AudioPlayerStateTable,
 | 
			
		||||
    PlaylistTable,
 | 
			
		||||
    PlaylistMediaTable,
 | 
			
		||||
  ],
 | 
			
		||||
)
 | 
			
		||||
class AppDatabase extends _$AppDatabase {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								lib/services/database/tables/audio_player_state.dart
									
									
									
									
									
										Executable file
									
								
							
							
						
						@@ -0,0 +1,27 @@
 | 
			
		||||
part of '../database.dart';
 | 
			
		||||
 | 
			
		||||
class AudioPlayerStateTable extends Table {
 | 
			
		||||
  IntColumn get id => integer().autoIncrement()();
 | 
			
		||||
  BoolColumn get playing => boolean()();
 | 
			
		||||
  TextColumn get loopMode => textEnum<PlaylistMode>()();
 | 
			
		||||
  BoolColumn get shuffled => boolean()();
 | 
			
		||||
  TextColumn get collections => text().map(const StringListConverter())();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PlaylistTable extends Table {
 | 
			
		||||
  IntColumn get id => integer().autoIncrement()();
 | 
			
		||||
  IntColumn get audioPlayerStateId =>
 | 
			
		||||
      integer().references(AudioPlayerStateTable, #id)();
 | 
			
		||||
  IntColumn get index => integer()();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PlaylistMediaTable extends Table {
 | 
			
		||||
  IntColumn get id => integer().autoIncrement()();
 | 
			
		||||
  IntColumn get playlistId => integer().references(PlaylistTable, #id)();
 | 
			
		||||
 | 
			
		||||
  TextColumn get uri => text()();
 | 
			
		||||
  TextColumn get extras =>
 | 
			
		||||
      text().nullable().map(const MapTypeConverter<String, dynamic>())();
 | 
			
		||||
  TextColumn get httpHeaders =>
 | 
			
		||||
      text().nullable().map(const MapTypeConverter<String, String>())();
 | 
			
		||||
}
 | 
			
		||||
@@ -13,24 +13,18 @@ abstract class KVStoreService {
 | 
			
		||||
    _sharedPreferences = await SharedPreferences.getInstance();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static bool get doneGettingStarted =>
 | 
			
		||||
      sharedPreferences.getBool('doneGettingStarted') ?? false;
 | 
			
		||||
  static Future<void> setDoneGettingStarted(bool value) async =>
 | 
			
		||||
      await sharedPreferences.setBool('doneGettingStarted', value);
 | 
			
		||||
 | 
			
		||||
  static bool get askedForBatteryOptimization =>
 | 
			
		||||
      sharedPreferences.getBool('askedForBatteryOptimization') ?? false;
 | 
			
		||||
      sharedPreferences.getBool('asked_for_battery_optimization') ?? false;
 | 
			
		||||
  static Future<void> setAskedForBatteryOptimization(bool value) async =>
 | 
			
		||||
      await sharedPreferences.setBool('askedForBatteryOptimization', value);
 | 
			
		||||
      await sharedPreferences.setBool('asked_for_battery_optimization', value);
 | 
			
		||||
 | 
			
		||||
  static List<String> get recentSearches =>
 | 
			
		||||
      sharedPreferences.getStringList('recentSearches') ?? [];
 | 
			
		||||
 | 
			
		||||
      sharedPreferences.getStringList('recent_searches') ?? [];
 | 
			
		||||
  static Future<void> setRecentSearches(List<String> value) async =>
 | 
			
		||||
      await sharedPreferences.setStringList('recentSearches', value);
 | 
			
		||||
      await sharedPreferences.setStringList('recent_searches', value);
 | 
			
		||||
 | 
			
		||||
  static WindowSize? get windowSize {
 | 
			
		||||
    final raw = sharedPreferences.getString('windowSize');
 | 
			
		||||
    final raw = sharedPreferences.getString('window_size');
 | 
			
		||||
 | 
			
		||||
    if (raw == null) {
 | 
			
		||||
      return null;
 | 
			
		||||
@@ -40,7 +34,7 @@ abstract class KVStoreService {
 | 
			
		||||
 | 
			
		||||
  static Future<void> setWindowSize(WindowSize value) async =>
 | 
			
		||||
      await sharedPreferences.setString(
 | 
			
		||||
        'windowSize',
 | 
			
		||||
        'window_size',
 | 
			
		||||
        jsonEncode(
 | 
			
		||||
          value.toJson(),
 | 
			
		||||
        ),
 | 
			
		||||
@@ -82,9 +76,4 @@ abstract class KVStoreService {
 | 
			
		||||
  static double get volume => sharedPreferences.getDouble('volume') ?? 1.0;
 | 
			
		||||
  static Future<void> setVolume(double value) async =>
 | 
			
		||||
      await sharedPreferences.setDouble('volume', value);
 | 
			
		||||
 | 
			
		||||
  static bool get hasMigratedToDrift =>
 | 
			
		||||
      sharedPreferences.getBool('hasMigratedToDrift') ?? false;
 | 
			
		||||
  static Future<void> setHasMigratedToDrift(bool value) async =>
 | 
			
		||||
      await sharedPreferences.setBool('hasMigratedToDrift', value);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,11 @@ class _PlaylistTrackListState extends State<PlaylistTrackList> {
 | 
			
		||||
            ),
 | 
			
		||||
            onTap: () {
 | 
			
		||||
              if (item == null) return;
 | 
			
		||||
              Get.find<AudioPlayerProvider>().load([item], autoPlay: true);
 | 
			
		||||
              Get.find<AudioPlayerProvider>().load(
 | 
			
		||||
                _tracks!,
 | 
			
		||||
                initialIndex: idx,
 | 
			
		||||
                autoPlay: true,
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,68 +1,68 @@
 | 
			
		||||
{
 | 
			
		||||
  "images" : [
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "16x16",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_16.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    "info": {
 | 
			
		||||
        "version": 1,
 | 
			
		||||
        "author": "xcode"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "16x16",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_32.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "32x32",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_32.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "32x32",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_64.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "128x128",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_128.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "128x128",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_256.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "256x256",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_256.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "256x256",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_512.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "512x512",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_512.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "512x512",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_1024.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "info" : {
 | 
			
		||||
    "version" : 1,
 | 
			
		||||
    "author" : "xcode"
 | 
			
		||||
  }
 | 
			
		||||
    "images": [
 | 
			
		||||
        {
 | 
			
		||||
            "size": "16x16",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_16.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "16x16",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_32.png",
 | 
			
		||||
            "scale": "2x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "32x32",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_32.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "32x32",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_64.png",
 | 
			
		||||
            "scale": "2x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "128x128",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_128.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "128x128",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_256.png",
 | 
			
		||||
            "scale": "2x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "256x256",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_256.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "256x256",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_512.png",
 | 
			
		||||
            "scale": "2x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "512x512",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_512.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "size": "512x512",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_1024.png",
 | 
			
		||||
            "scale": "2x"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 47 KiB  | 
| 
		 Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 3.2 KiB  | 
| 
		 Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 320 B  | 
| 
		 Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 7.9 KiB  | 
| 
		 Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 621 B  | 
| 
		 Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 20 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.4 KiB  | 
@@ -419,6 +419,14 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.4.1"
 | 
			
		||||
  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:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								pubspec.yaml
									
									
									
									
									
								
							
							
						
						@@ -56,7 +56,6 @@ dependencies:
 | 
			
		||||
  piped_client: ^0.1.1
 | 
			
		||||
  flutter_broadcasts: ^0.4.0
 | 
			
		||||
  audio_session: ^0.1.21
 | 
			
		||||
  shared_preferences: ^2.3.2
 | 
			
		||||
  audio_service: ^0.18.15
 | 
			
		||||
  smtc_windows: ^0.1.3
 | 
			
		||||
  win32_registry: ^1.1.4
 | 
			
		||||
@@ -82,6 +81,7 @@ dependencies:
 | 
			
		||||
      url: https://github.com/KRTirtho/scrobblenaut.git
 | 
			
		||||
      ref: dart-3-support
 | 
			
		||||
  dismissible_page: ^1.0.2
 | 
			
		||||
  shared_preferences: ^2.3.2
 | 
			
		||||
 | 
			
		||||
dev_dependencies:
 | 
			
		||||
  flutter_test:
 | 
			
		||||
@@ -95,6 +95,7 @@ dev_dependencies:
 | 
			
		||||
  flutter_lints: ^4.0.0
 | 
			
		||||
  drift_dev: ^2.20.1
 | 
			
		||||
  build_runner: ^2.4.12
 | 
			
		||||
  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
 | 
			
		||||
@@ -137,3 +138,19 @@ flutter:
 | 
			
		||||
  #
 | 
			
		||||
  # For details regarding fonts from package dependencies,
 | 
			
		||||
  # see https://flutter.dev/to/font-from-package
 | 
			
		||||
 | 
			
		||||
flutter_launcher_icons:
 | 
			
		||||
  android: false
 | 
			
		||||
  ios: true
 | 
			
		||||
  image_path: "assets/icon.png"
 | 
			
		||||
  min_sdk_android: 21 # android min sdk min:16, default 21
 | 
			
		||||
  web:
 | 
			
		||||
    generate: true
 | 
			
		||||
    image_path: "assets/icon.png"
 | 
			
		||||
  windows:
 | 
			
		||||
    generate: true
 | 
			
		||||
    image_path: "assets/icon.png"
 | 
			
		||||
    icon_size: 256
 | 
			
		||||
  macos:
 | 
			
		||||
    generate: true
 | 
			
		||||
    image_path: "assets/icon.png"
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								web/favicon.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 917 B After Width: | Height: | Size: 320 B  | 
| 
		 Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.5 KiB  | 
| 
		 Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 20 KiB  | 
| 
		 Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB  | 
| 
		 Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB  | 
| 
		 Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 7.9 KiB  |