diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml index c27a3463..6a1c713f 100644 --- a/.github/workflows/spotube-release-binary.yml +++ b/.github/workflows/spotube-release-binary.yml @@ -87,6 +87,12 @@ jobs: uses: dtolnay/rust-toolchain@stable with: toolchain: stable + + - name: Install Xcode + if: ${{matrix.platform == 'ios'}} + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.1' - name: Install ${{matrix.platform}} dependencies run: | diff --git a/.gitignore b/.gitignore index f9bd15f8..97b5c03c 100644 --- a/.gitignore +++ b/.gitignore @@ -79,4 +79,6 @@ android/key.properties tm.json # FVM Version Cache -.fvm/ \ No newline at end of file +.fvm/ + +android/build diff --git a/.vscode/settings.json b/.vscode/settings.json index 11fae610..1f47bada 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,5 +27,5 @@ "README.md": "LICENSE,CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,CONTRIBUTION.md,CHANGELOG.md,PRIVACY_POLICY.md", "*.dart": "${capture}.g.dart,${capture}.freezed.dart" }, - "dart.flutterSdkPath": ".fvm/flutter_sdk" + "dart.flutterSdkPath": ".fvm/versions/3.27.0" } \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 7c1a6356..74f6efea 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -28,8 +28,10 @@ if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) } +def composeVersion = "1.4.8" + android { - namespace "dev.krtirtho.spotube" + namespace "oss.krtirtho.spotube" compileSdkVersion 35 @@ -48,6 +50,14 @@ android { main.java.srcDirs += 'src/main/kotlin' } + buildFeatures { + compose true + } + + composeOptions { + kotlinCompilerExtensionVersion "$composeVersion" // Correlates with org.jetbrains.kotlin.android plugin in settings.gradle + } + defaultConfig { applicationId "oss.krtirtho.spotube" minSdkVersion 24 @@ -65,6 +75,7 @@ android { storePassword keystoreProperties['storePassword'] } } + buildTypes { release { signingConfig signingConfigs.release @@ -98,15 +109,28 @@ android { } } + packagingOptions { + resources.excludes += "DebugProbesKt.bin" + } } flutter { source '../..' } +def glanceVersion = "1.1.1" dependencies { implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' - // other deps so just ignore implementation 'com.android.support:multidex:2.0.1' + + implementation "androidx.glance:glance-appwidget:$glanceVersion" + implementation "androidx.glance:glance-appwidget-preview:$glanceVersion" + implementation "androidx.glance:glance-preview:$glanceVersion" + implementation "androidx.glance:glance-material3:$glanceVersion" + implementation "androidx.glance:glance-material:$glanceVersion" + implementation "androidx.work:work-runtime-ktx:2.8.1" + + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3" + implementation 'com.google.code.gson:gson:2.11.0' } \ No newline at end of file diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 116bc22f..ee867c13 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -1 +1,8 @@ --keep class androidx.lifecycle.DefaultLifecycleObserver \ No newline at end of file +-keep class androidx.lifecycle.DefaultLifecycleObserver + +-keepnames class kotlinx.serialization.** { *; } +-keepnames class oss.krtirtho.spotube.glance.models.** { *; } +-keep @kotlinx.serialization.Serializable class * +-keepclassmembers class ** { + @kotlinx.serialization.* ; +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 78f744c4..8c676896 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,38 +17,36 @@ + android:usesCleartextTraffic="true"> + android:name="io.flutter.embedding.android.EnableImpeller" + android:value="true" /> --> + android:windowSoftInputMode="adjustResize"> + Specifies an Android theme to apply to this Activity as soon as + the Android process has started. This theme is visible to the user + while the Flutter UI initializes. After that, this theme continues + to determine the Window background behind the Flutter UI. + --> + android:resource="@style/NormalTheme" /> + @@ -56,12 +54,13 @@ + + + android:scheme="https" /> @@ -72,23 +71,30 @@ + + + + + - + - @@ -96,11 +102,40 @@ - + + + + + + + + + + + + + + + + + - + This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> + \ No newline at end of file diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/HomePlayerWidget.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/HomePlayerWidget.kt new file mode 100644 index 00000000..a04a0508 --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/HomePlayerWidget.kt @@ -0,0 +1,216 @@ +package oss.krtirtho.spotube.glance + +import HomeWidgetGlanceState +import HomeWidgetGlanceStateDefinition +import android.content.Context +import android.graphics.drawable.Icon +import android.net.Uri +import android.util.Log +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.glance.GlanceId +import androidx.glance.GlanceModifier +import androidx.glance.GlanceTheme +import androidx.glance.Image +import androidx.glance.ImageProvider +import androidx.glance.LocalSize +import androidx.glance.action.ActionParameters +import androidx.glance.action.actionParametersOf +import androidx.glance.action.clickable +import androidx.glance.background +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.SizeMode +import androidx.glance.appwidget.action.ActionCallback +import androidx.glance.appwidget.action.actionRunCallback +import androidx.glance.appwidget.background +import androidx.glance.appwidget.components.CircleIconButton +import androidx.glance.appwidget.components.Scaffold +import androidx.glance.appwidget.cornerRadius +import androidx.glance.appwidget.provideContent +import androidx.glance.background +import androidx.glance.currentState +import androidx.glance.layout.Alignment +import androidx.glance.layout.Box +import androidx.glance.layout.Column +import androidx.glance.layout.ContentScale +import androidx.glance.layout.Row +import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxSize +import androidx.glance.layout.fillMaxWidth +import androidx.glance.layout.padding +import androidx.glance.layout.size +import androidx.glance.preview.ExperimentalGlancePreviewApi +import androidx.glance.preview.Preview +import androidx.glance.state.GlanceStateDefinition +import com.google.gson.Gson +import es.antonborri.home_widget.HomeWidgetBackgroundIntent +import es.antonborri.home_widget.actionStartActivity +import oss.krtirtho.spotube.MainActivity +import oss.krtirtho.spotube.glance.models.Track +import oss.krtirtho.spotube.glance.widgets.FlutterAssetImageProvider +import oss.krtirtho.spotube.glance.widgets.TrackDetailsView +import oss.krtirtho.spotube.glance.widgets.TrackProgress + +val gson = Gson() +val serverAddressKey = ActionParameters.Key("serverAddress") + +class Breakpoints { + companion object { + val SMALL_SQUARE = DpSize(100.dp, 100.dp) + val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp) + val BIG_SQUARE = DpSize(250.dp, 250.dp) + } +} + +class HomePlayerWidget : GlanceAppWidget() { + + override val sizeMode = SizeMode.Responsive( + setOf( + Breakpoints.SMALL_SQUARE, + Breakpoints.HORIZONTAL_RECTANGLE, + Breakpoints.BIG_SQUARE + ) + ) + + override val stateDefinition: GlanceStateDefinition<*>? + get() = HomeWidgetGlanceStateDefinition() + + override suspend fun provideGlance(context: Context, id: GlanceId) { + provideContent { + GlanceContent(context, currentState()) + } + } + + + @OptIn(ExperimentalGlancePreviewApi::class) + @Preview(widthDp = 100, heightDp = 100) + @Composable + private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) { + val prefs = currentState.preferences + val size = LocalSize.current + + val activeTrackStr = prefs.getString("activeTrack", null) + + val isPlaying = prefs.getBoolean("isPlaying", false) + val playbackServerAddress = prefs.getString("playbackServerAddress", null) ?: "" + + var activeTrack: Track? = null + if (activeTrackStr != null) { + activeTrack = gson.fromJson(activeTrackStr, Track::class.java) + } + + + val playIcon = Icon.createWithResource(context, R.drawable.ic_media_play); + val pauseIcon = Icon.createWithResource(context, R.drawable.ic_media_pause); + val previousIcon = Icon.createWithResource(context, R.drawable.ic_media_previous); + val nextIcon = Icon.createWithResource(context, R.drawable.ic_media_next); + + GlanceTheme { + Box( + modifier = GlanceModifier + .fillMaxSize() + .cornerRadius(8.dp) + .background( + color = GlanceTheme.colors.surface.getColor(context) + ) + .clickable { + actionStartActivity(context) + } + , + ) { + Image( + provider = FlutterAssetImageProvider( + context, + "assets/backgrounds/xmas-effect.png" + ), + contentDescription = "Background", + modifier = GlanceModifier + .fillMaxSize(), + contentScale = ContentScale.Crop + ) + Box( + modifier = GlanceModifier + .background( + color = + GlanceTheme.colors.surface.getColor(context) + .copy(alpha = 0.5f), + ) + .fillMaxSize(), + ) {} + Column( + modifier = GlanceModifier.padding(top = 10.dp, start = 10.dp, end = 10.dp) + ) { + Row(verticalAlignment = Alignment.Vertical.CenterVertically) { + TrackDetailsView(activeTrack) + } + Spacer(modifier = GlanceModifier.size(6.dp)) + if (size != Breakpoints.SMALL_SQUARE) { + TrackProgress(prefs) + } + Spacer(modifier = GlanceModifier.size(6.dp)) + Row( + modifier = GlanceModifier.fillMaxWidth(), + horizontalAlignment = Alignment.Horizontal.CenterHorizontally + ) { + CircleIconButton( + imageProvider = ImageProvider(previousIcon), + contentDescription = "Previous", + onClick = actionRunCallback( + parameters = actionParametersOf(serverAddressKey to playbackServerAddress) + ) + ) + Spacer(modifier = GlanceModifier.size(6.dp)) + CircleIconButton( + imageProvider = + if (isPlaying) ImageProvider(pauseIcon) + else ImageProvider(playIcon), + contentDescription = "Play/Pause", + onClick = actionRunCallback( + parameters = actionParametersOf(serverAddressKey to playbackServerAddress) + ) + ) + Spacer(modifier = GlanceModifier.size(6.dp)) + CircleIconButton( + imageProvider = ImageProvider(nextIcon), + contentDescription = "Previous", + onClick = actionRunCallback( + parameters = actionParametersOf( + serverAddressKey to playbackServerAddress + ) + ) + ) + } + } + } + } + } +} + +class PlayPauseAction : InteractiveAction("toggle-playback") +class NextAction : InteractiveAction("next") +class PreviousAction : InteractiveAction("previous") + + +abstract class InteractiveAction(val command: String) : ActionCallback { + override suspend fun onAction( + context: Context, + glanceId: GlanceId, + parameters: ActionParameters + ) { + val serverAddress = parameters[serverAddressKey] ?: "" + + Log.d("HomePlayerWidget", "Sending command $command to $serverAddress") + + if (serverAddress == null || serverAddress.isEmpty()) { + return + } + + + val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast( + context, + Uri.parse("spotube://playback/$command?serverAddress=$serverAddress") + ) + backgroundIntent.send() + } +} diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/HomePlayerWidgetReceiver.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/HomePlayerWidgetReceiver.kt new file mode 100644 index 00000000..2d23c64f --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/HomePlayerWidgetReceiver.kt @@ -0,0 +1,7 @@ +package oss.krtirtho.spotube.glance + +import HomeWidgetGlanceWidgetReceiver + +class HomePlayerWidgetReceiver : HomeWidgetGlanceWidgetReceiver() { + override val glanceAppWidget = HomePlayerWidget() +} diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/AlbumSimple.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/AlbumSimple.kt new file mode 100644 index 00000000..4edd69f6 --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/AlbumSimple.kt @@ -0,0 +1,40 @@ +package oss.krtirtho.spotube.glance.models + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +@Serializable +data class AlbumSimple( + @SerializedName("album_type") + val albumType: AlbumType?, + + @SerializedName("available_markets") + val availableMarkets: List?, + + val href: String?, + val id: String?, + val images: List?, + val name: String?, + + @SerializedName("release_date") + val releaseDate: String?, + + @SerializedName("release_date_precision") + val releaseDatePrecision: DatePrecision?, + + val type: String?, + val uri: String?, +) + +@Serializable +enum class AlbumType { + album, + single, + compilation +} + +enum class DatePrecision { + year, + month, + day +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Artist.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Artist.kt new file mode 100644 index 00000000..ef43ecc8 --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Artist.kt @@ -0,0 +1,25 @@ +package oss.krtirtho.spotube.glance.models + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +@Serializable +data class Artist( + val href: String?, + val id: String?, + val name: String?, + val type: String?, + val uri: String?, + + val followers: Followers?, + val genres: List?, + val images: List?, + + @SerializedName("popularity") + val popularity: Int? +) + +@Serializable +data class Followers( + val total: Int? +) diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Image.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Image.kt new file mode 100644 index 00000000..de7d5521 --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Image.kt @@ -0,0 +1,10 @@ +package oss.krtirtho.spotube.glance.models + +import kotlinx.serialization.Serializable + +@Serializable +data class Image( + val height: Int?, + val width: Int?, + val path: String, +) \ No newline at end of file diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Track.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Track.kt new file mode 100644 index 00000000..717b790f --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/models/Track.kt @@ -0,0 +1,37 @@ +package oss.krtirtho.spotube.glance.models + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable +import kotlin.time.Duration.Companion.milliseconds + +@Serializable +data class Track( + val album: AlbumSimple?, val artists: List?, + + @SerializedName("available_markets") val availableMarkets: List?, + + @SerializedName("disc_number") val discNumber: Int?, + + @SerializedName("duration_ms") val durationMs: Int, + + val explicit: Boolean?, val href: String?, val id: String?, + + @SerializedName("is_playable") val isPlayable: Boolean?, + + val name: String?, + + @SerializedName("popularity") val popularity: Int?, + + @SerializedName("preview_url") val previewUrl: String?, + + @SerializedName("track_number") val trackNumber: Int?, + + val type: String?, val uri: String? +) { + val duration: kotlin.time.Duration + get() = durationMs.toLong().milliseconds +} + +enum class Market { + AD, AE, AF, AG, AI, AL, AM, AO, AQ, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BL, BM, BN, BO, BQ, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MF, MG, MH, MK, ML, MM, MN, MO, MP, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PS, PT, PW, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WS, XK, YE, YT, ZA, ZM, ZW, +} diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/Base64ImageProvider.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/Base64ImageProvider.kt new file mode 100644 index 00000000..79339cea --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/Base64ImageProvider.kt @@ -0,0 +1,14 @@ +package oss.krtirtho.spotube.glance.widgets + +import android.graphics.BitmapFactory +import android.util.Base64 +import androidx.glance.ImageProvider + +@Suppress("FunctionName") +fun Base64ImageProvider(base64: String): ImageProvider { + var bytes = Base64.decode(base64, Base64.DEFAULT); + + var bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size); + + return ImageProvider(bitmap) +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/FlutterAssetImageProvider.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/FlutterAssetImageProvider.kt new file mode 100644 index 00000000..ad51ca3c --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/FlutterAssetImageProvider.kt @@ -0,0 +1,14 @@ +package oss.krtirtho.spotube.glance.widgets + +import android.content.Context +import android.graphics.BitmapFactory +import androidx.glance.ImageProvider + +@Suppress("FunctionName") +fun FlutterAssetImageProvider(context: Context, path: String): ImageProvider { + var inputStream = context.assets.open("flutter_assets/$path") + + return ImageProvider( + BitmapFactory.decodeStream(inputStream) + ) +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/TrackDetailsView.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/TrackDetailsView.kt new file mode 100644 index 00000000..fdfe8e4b --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/TrackDetailsView.kt @@ -0,0 +1,78 @@ +package oss.krtirtho.spotube.glance.widgets + +import android.graphics.BitmapFactory +import android.net.Uri +import android.util.Log +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.glance.GlanceModifier +import androidx.glance.GlanceTheme +import androidx.glance.Image +import androidx.glance.ImageProvider +import androidx.glance.LocalContext +import androidx.glance.LocalSize +import androidx.glance.appwidget.cornerRadius +import androidx.glance.layout.Alignment +import androidx.glance.layout.Row +import androidx.glance.layout.Column +import androidx.glance.layout.ContentScale +import androidx.glance.layout.Spacer +import androidx.glance.layout.size +import androidx.glance.text.FontWeight +import androidx.glance.text.Text +import androidx.glance.text.TextStyle +import oss.krtirtho.spotube.glance.Breakpoints +import oss.krtirtho.spotube.glance.models.Track + +@Composable +fun TrackDetailsView(activeTrack: Track?) { + val context = LocalContext.current + + val size = LocalSize.current + + val artistStr = activeTrack?.artists?.map { it.name }?.joinToString(", ") ?: "" + val imgLocalPath = activeTrack?.album?.images?.get(0)?.path; + val title = activeTrack?.name ?: "" + + + Image( + provider = + if (imgLocalPath == null) + ImageProvider( + BitmapFactory.decodeResource( + context.resources, + android.R.drawable.ic_delete + ) + ) + else ImageProvider(BitmapFactory.decodeFile(imgLocalPath)), + contentDescription = "Album Art", + modifier = GlanceModifier.cornerRadius(8.dp) + .size( + if (size.height < 200.dp) 50.dp + else 100.dp + ), + contentScale = ContentScale.Fit + ) + Spacer(modifier = GlanceModifier.size(6.dp)) + Column { + Text( + text = title, + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = GlanceTheme.colors.onBackground + ), + ) + if (size != Breakpoints.SMALL_SQUARE) { + Spacer(modifier = GlanceModifier.size(6.dp)) + Text( + text = artistStr, + style = TextStyle( + fontSize = 14.sp, + color = GlanceTheme.colors.onBackground + ), + ) + } + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/TrackProgress.kt b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/TrackProgress.kt new file mode 100644 index 00000000..b54059b1 --- /dev/null +++ b/android/app/src/main/kotlin/oss/krtirtho/spotube/glance/widgets/TrackProgress.kt @@ -0,0 +1,77 @@ +package oss.krtirtho.spotube.glance.widgets + +import android.content.SharedPreferences +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import androidx.glance.GlanceModifier +import androidx.glance.GlanceTheme +import androidx.glance.LocalSize +import androidx.glance.appwidget.LinearProgressIndicator +import androidx.glance.layout.Column +import androidx.glance.layout.Row +import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxWidth +import androidx.glance.layout.size +import androidx.glance.text.Text +import androidx.glance.text.TextStyle +import kotlin.math.max +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds +import oss.krtirtho.spotube.glance.Breakpoints + +fun Duration.format(): String { + return this.toComponents { hour, minutes, seconds, nanoseconds -> + var paddedSeconds = seconds.toString().padStart(2, '0') + var paddedMinutes = minutes.toString().padStart(2, '0') + var paddedHour = hour.toString().padStart(2, '0') + if (hour == 0L) { + "$paddedMinutes:$paddedSeconds" + } else { + "$paddedHour:$paddedMinutes:$paddedSeconds" + } + } +} + +@Composable +fun TrackProgress(prefs: SharedPreferences) { + val size = LocalSize.current + val position = prefs.getInt("position", 0).seconds + var duration = prefs.getInt("duration", 0).seconds + + var progress = position.inWholeSeconds.toFloat() / max(duration.inWholeSeconds.toFloat(), 1.0f) + + var textStyle = + TextStyle( + color = GlanceTheme.colors.onBackground, + ) + + if (size == Breakpoints.HORIZONTAL_RECTANGLE) { + Row(modifier = GlanceModifier.fillMaxWidth()) { + Text(text = position.format(), style = textStyle) + Spacer(modifier = GlanceModifier.size(6.dp)) + LinearProgressIndicator( + progress = progress, + modifier = GlanceModifier.defaultWeight(), + color = GlanceTheme.colors.primary, + backgroundColor = GlanceTheme.colors.primaryContainer, + ) + Spacer(modifier = GlanceModifier.size(6.dp)) + Text(text = duration.format(), style = textStyle) + } + } else { + Column(modifier = GlanceModifier.fillMaxWidth()) { + LinearProgressIndicator( + progress = progress, + modifier = GlanceModifier.fillMaxWidth(), + color = GlanceTheme.colors.primary, + backgroundColor = GlanceTheme.colors.primaryContainer, + ) + Spacer(modifier = GlanceModifier.size(6.dp)) + Row(modifier = GlanceModifier.fillMaxWidth()) { + Text(text = position.format(), style = textStyle) + Spacer(modifier = GlanceModifier.defaultWeight()) + Text(text = duration.format(), style = textStyle) + } + } + } +} diff --git a/android/app/src/main/res/xml/home_player_widget_config.xml b/android/app/src/main/res/xml/home_player_widget_config.xml new file mode 100644 index 00000000..c8ec7048 --- /dev/null +++ b/android/app/src/main/res/xml/home_player_widget_config.xml @@ -0,0 +1,7 @@ + + diff --git a/android/build.gradle b/android/build.gradle index bc157bd1..8f31e8ca 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -15,4 +15,4 @@ subprojects { tasks.register("clean", Delete) { delete rootProject.buildDir -} +} \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index a1961f52..1e8ffbe3 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -18,8 +18,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.7.0" apply false + id "com.android.application" version '8.7.0' apply false id "org.jetbrains.kotlin.android" version "1.8.22" apply false } -include ":app" \ No newline at end of file +include ':app' \ No newline at end of file diff --git a/assets/backgrounds/xmas-effect.png b/assets/backgrounds/xmas-effect.png new file mode 100644 index 00000000..e7c8eeef Binary files /dev/null and b/assets/backgrounds/xmas-effect.png differ diff --git a/ios/HomePlayerWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/HomePlayerWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/ios/HomePlayerWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/HomePlayerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/HomePlayerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/ios/HomePlayerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/HomePlayerWidget/Assets.xcassets/Contents.json b/ios/HomePlayerWidget/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/ios/HomePlayerWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/HomePlayerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/ios/HomePlayerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/ios/HomePlayerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/HomePlayerWidget/HomePlayerWidget.swift b/ios/HomePlayerWidget/HomePlayerWidget.swift new file mode 100644 index 00000000..8808aae1 --- /dev/null +++ b/ios/HomePlayerWidget/HomePlayerWidget.swift @@ -0,0 +1,86 @@ +// +// HomePlayerWidget.swift +// HomePlayerWidget +// +// Created by Kingkor Roy Tirtho on 15/12/24. +// + +import WidgetKit +import SwiftUI + +private let widgetGroupId = "group.spotube_home_player_widget" + +struct Provider: TimelineProvider { + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date(), emoji: "😀") + } + + func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { + let entry = SimpleEntry(date: Date(), emoji: "😀") + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { + var entries: [SimpleEntry] = [] + + // Generate a timeline consisting of five entries an hour apart, starting from the current date. + let currentDate = Date() + for hourOffset in 0 ..< 5 { + let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! + let entry = SimpleEntry(date: entryDate, emoji: "😀") + entries.append(entry) + } + + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } + +// func relevances() async -> WidgetRelevances { +// // Generate a list containing the contexts this widget is relevant in. +// } +} + +struct SimpleEntry: TimelineEntry { + let date: Date + let emoji: String +} + +struct HomePlayerWidgetEntryView : View { + var entry: Provider.Entry + + var body: some View { + VStack { + Text("Time:") + Text(entry.date, style: .time) + + Text("Emoji:") + Text(entry.emoji) + } + } +} + +struct HomePlayerWidget: Widget { + let kind: String = "HomePlayerWidget" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: Provider()) { entry in + if #available(iOS 17.0, *) { + HomePlayerWidgetEntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } else { + HomePlayerWidgetEntryView(entry: entry) + .padding() + .background() + } + } + .configurationDisplayName("My Widget") + .description("This is an example widget.") + } +} + +#Preview(as: .systemSmall) { + HomePlayerWidget() +} timeline: { + SimpleEntry(date: .now, emoji: "😀") + SimpleEntry(date: .now, emoji: "🤩") +} diff --git a/ios/HomePlayerWidget/HomePlayerWidgetBundle.swift b/ios/HomePlayerWidget/HomePlayerWidgetBundle.swift new file mode 100644 index 00000000..68158b53 --- /dev/null +++ b/ios/HomePlayerWidget/HomePlayerWidgetBundle.swift @@ -0,0 +1,16 @@ +// +// HomePlayerWidgetBundle.swift +// HomePlayerWidget +// +// Created by Kingkor Roy Tirtho on 15/12/24. +// + +import WidgetKit +import SwiftUI + +@main +struct HomePlayerWidgetBundle: WidgetBundle { + var body: some Widget { + HomePlayerWidget() + } +} diff --git a/ios/HomePlayerWidget/Info.plist b/ios/HomePlayerWidget/Info.plist new file mode 100644 index 00000000..0f118fb7 --- /dev/null +++ b/ios/HomePlayerWidget/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/ios/HomePlayerWidgetExtension.entitlements b/ios/HomePlayerWidgetExtension.entitlements new file mode 100644 index 00000000..58165678 --- /dev/null +++ b/ios/HomePlayerWidgetExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.spotube_home_player_widget + + + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 104ff767..31ffe436 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -64,6 +64,8 @@ PODS: - Flutter - flutter_sharing_intent (0.0.1): - Flutter + - home_widget (0.0.1): + - Flutter - image_picker_ios (0.0.1): - Flutter - integration_test (0.0.1): @@ -106,12 +108,15 @@ PODS: - sqlite3/common - sqlite3_flutter_libs (0.0.1): - Flutter - - sqlite3 (~> 3.47.0) + - FlutterMacOS + - sqlite3 (~> 3.47.1) - sqlite3/dbstatvtab - sqlite3/fts5 - sqlite3/perf-threadsafe - sqlite3/rtree - SwiftyGif (5.4.4) + - system_theme (0.0.1): + - Flutter - url_launcher_ios (0.0.1): - Flutter @@ -130,6 +135,7 @@ DEPENDENCIES: - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_sharing_intent (from `.symlinks/plugins/flutter_sharing_intent/ios`) + - home_widget (from `.symlinks/plugins/home_widget/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) - media_kit_libs_ios_audio (from `.symlinks/plugins/media_kit_libs_ios_audio/ios`) @@ -141,7 +147,8 @@ DEPENDENCIES: - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) + - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`) + - system_theme (from `.symlinks/plugins/system_theme/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) SPEC REPOS: @@ -182,6 +189,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_secure_storage/ios" flutter_sharing_intent: :path: ".symlinks/plugins/flutter_sharing_intent/ios" + home_widget: + :path: ".symlinks/plugins/home_widget/ios" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" integration_test: @@ -205,7 +214,9 @@ EXTERNAL SOURCES: sqflite_darwin: :path: ".symlinks/plugins/sqflite_darwin/darwin" sqlite3_flutter_libs: - :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" + :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin" + system_theme: + :path: ".symlinks/plugins/system_theme/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" @@ -226,6 +237,7 @@ SPEC CHECKSUMS: flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 flutter_sharing_intent: e35380d0e1501d7111dbb7e46d5ac6339da6da98 + home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 media_kit_libs_ios_audio: 8f39d96a9c630685dfb844c289bd1d114c486fb3 @@ -240,8 +252,9 @@ SPEC CHECKSUMS: shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d sqlite3: 1e522f0938463e44b7faf50393b40bdc1e1e456d - sqlite3_flutter_libs: b55ef23cfafea5318ae5081e0bf3fbbce8417c94 + sqlite3_flutter_libs: 1b4e98da20ebd4e9b1240269b78cdcf492dbe9f3 SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f + system_theme: bfc1b0913d08f38d8c6bbe94b202a58df599d9f7 url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe PODFILE CHECKSUM: 0659b64ac6e9e96b61d8550decffa8bff51a957e diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 34793f68..63871a3d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -36,8 +36,21 @@ B536BDD62B4060B3009B3CE4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; B536BDD72B4060B3009B3CE4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; C36A05AD330BBFAED75A62D5 /* Pods_dev.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4238A4985255EC9F93067739 /* Pods_dev.framework */; }; + E612EC3B2D0F07A90022720C /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E612EC3A2D0F07A90022720C /* WidgetKit.framework */; }; + E612EC3D2D0F07A90022720C /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E612EC3C2D0F07A90022720C /* SwiftUI.framework */; }; + E612EC482D0F07AD0022720C /* HomePlayerWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E612EC392D0F07A90022720C /* HomePlayerWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + E612EC462D0F07AD0022720C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = E612EC382D0F07A80022720C; + remoteInfo = HomePlayerWidgetExtension; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -79,6 +92,17 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + E612EC492D0F07AD0022720C /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + E612EC482D0F07AD0022720C /* HomePlayerWidgetExtension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -148,6 +172,14 @@ D32BAE0F55672DD7669755B8 /* Pods-Runner.debug-stable.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-stable.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-stable.xcconfig"; sourceTree = ""; }; D9A69004587D01A7C68666CF /* Pods-dev.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-dev.release.xcconfig"; path = "Target Support Files/Pods-dev/Pods-dev.release.xcconfig"; sourceTree = ""; }; E0EAB4380EE7C7EA7A350B6F /* Pods-stable.release-nightly.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-stable.release-nightly.xcconfig"; path = "Target Support Files/Pods-stable/Pods-stable.release-nightly.xcconfig"; sourceTree = ""; }; + E612EC392D0F07A90022720C /* HomePlayerWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = HomePlayerWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + E612EC3A2D0F07A90022720C /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + E612EC3C2D0F07A90022720C /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + E6F17DB92D0F34E500BC2FA2 /* HomePlayerWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HomePlayerWidgetExtension.entitlements; sourceTree = ""; }; + E6F17DBA2D0F352C00BC2FA2 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + E6F17DBB2D0F356700BC2FA2 /* stable.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = stable.entitlements; sourceTree = ""; }; + E6F17DBC2D0F357500BC2FA2 /* dev.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = dev.entitlements; sourceTree = ""; }; + E6F17DBD2D0F357F00BC2FA2 /* nightly.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = nightly.entitlements; sourceTree = ""; }; E81F11471FD7D807286E33D6 /* Pods-dev.debug-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-dev.debug-dev.xcconfig"; path = "Target Support Files/Pods-dev/Pods-dev.debug-dev.xcconfig"; sourceTree = ""; }; EB7783C1029CEC13F4B05D36 /* Pods-nightly.debug-nightly.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-nightly.debug-nightly.xcconfig"; path = "Target Support Files/Pods-nightly/Pods-nightly.debug-nightly.xcconfig"; sourceTree = ""; }; EBBED0A8DE0D0E230CD03613 /* Pods-dev.release-stable.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-dev.release-stable.xcconfig"; path = "Target Support Files/Pods-dev/Pods-dev.release-stable.xcconfig"; sourceTree = ""; }; @@ -155,6 +187,20 @@ F7E9EBDD27997A73A4D38EE1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + E612EC562D0F07AD0022720C /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = E612EC382D0F07A80022720C /* HomePlayerWidgetExtension */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + E612EC3E2D0F07A90022720C /* HomePlayerWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (E612EC562D0F07AD0022720C /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = HomePlayerWidget; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -189,6 +235,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E612EC362D0F07A80022720C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E612EC3D2D0F07A90022720C /* SwiftUI.framework in Frameworks */, + E612EC3B2D0F07A90022720C /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -199,6 +254,8 @@ 4238A4985255EC9F93067739 /* Pods_dev.framework */, BAC36FC304DBD4E8A8C00694 /* Pods_nightly.framework */, B5F91A319C771EEC978B238A /* Pods_stable.framework */, + E612EC3A2D0F07A90022720C /* WidgetKit.framework */, + E612EC3C2D0F07A90022720C /* SwiftUI.framework */, ); name = Frameworks; sourceTree = ""; @@ -272,8 +329,13 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + E6F17DBD2D0F357F00BC2FA2 /* nightly.entitlements */, + E6F17DBC2D0F357500BC2FA2 /* dev.entitlements */, + E6F17DBB2D0F356700BC2FA2 /* stable.entitlements */, + E6F17DB92D0F34E500BC2FA2 /* HomePlayerWidgetExtension.entitlements */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, + E612EC3E2D0F07A90022720C /* HomePlayerWidget */, 97C146EF1CF9000F007C117D /* Products */, 67CBFE209DF24C94A9837AD5 /* Pods */, 0E0B839C4E103F896209E822 /* Frameworks */, @@ -290,6 +352,7 @@ B536BDA02B405DB1009B3CE4 /* stable.app */, B536BDBF2B405FDE009B3CE4 /* dev.app */, B536BDE42B4060B3009B3CE4 /* nightly.app */, + E612EC392D0F07A90022720C /* HomePlayerWidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -297,6 +360,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + E6F17DBA2D0F352C00BC2FA2 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -325,10 +389,13 @@ 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 6E9FEF583EA597C8B76255B2 /* [CP] Embed Pods Frameworks */, 46F6EB27C31C41D86428A28B /* [CP] Copy Pods Resources */, + E612EC492D0F07AD0022720C /* Embed Foundation Extensions */, + E63F9CBC2D10709D00CD9E72 /* ShellScript */, ); buildRules = ( ); dependencies = ( + E612EC472D0F07AD0022720C /* PBXTargetDependency */, ); name = Runner; productName = Runner; @@ -404,12 +471,35 @@ productReference = B536BDE42B4060B3009B3CE4 /* nightly.app */; productType = "com.apple.product-type.application"; }; + E612EC382D0F07A80022720C /* HomePlayerWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = E612EC572D0F07AD0022720C /* Build configuration list for PBXNativeTarget "HomePlayerWidgetExtension" */; + buildPhases = ( + E612EC352D0F07A80022720C /* Sources */, + E612EC362D0F07A80022720C /* Frameworks */, + E612EC372D0F07A80022720C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + E612EC3E2D0F07A90022720C /* HomePlayerWidget */, + ); + name = HomePlayerWidgetExtension; + packageProductDependencies = ( + ); + productName = HomePlayerWidgetExtension; + productReference = E612EC392D0F07A90022720C /* HomePlayerWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1620; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { @@ -417,6 +507,9 @@ CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; }; + E612EC382D0F07A80022720C = { + CreatedOnToolsVersion = 16.2; + }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; @@ -436,6 +529,7 @@ B536BD8C2B405DB1009B3CE4 /* stable */, B536BDAB2B405FDE009B3CE4 /* dev */, B536BDCD2B4060B3009B3CE4 /* nightly */, + E612EC382D0F07A80022720C /* HomePlayerWidgetExtension */, ); }; /* End PBXProject section */ @@ -485,6 +579,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E612EC372D0F07A80022720C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -685,7 +786,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; A6D446F111DE4C4A202BE7F7 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -814,6 +915,23 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-nightly/Pods-nightly-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + E63F9CBC2D10709D00CD9E72 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a sgeneratedPath=\"$SRCROOT/Flutter/Generated.xcconfig\"\n\n# Read and trim versionNumber and buildNumber\nversionNumber=$(grep FLUTTER_BUILD_NAME \"$generatedPath\" | cut -d '=' -f2 | xargs)\nbuildNumber=$(grep FLUTTER_BUILD_NUMBER \"$generatedPath\" | cut -d '=' -f2 | xargs)\n\ninfoPlistPath=\"$SRCROOT/HomePlayerWidget/Info.plist\"\n\n# Check and add CFBundleVersion if it does not exist\n/usr/libexec/PlistBuddy -c \"Print :CFBundleVersion\" \"$infoPlistPath\" 2>/dev/null\nif [ $? != 0 ]; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleVersion string $buildNumber\" \"$infoPlistPath\"\nelse\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$infoPlistPath\"\nfi\n\n# Check and add CFBundleShortVersionString if it does not exist\n/usr/libexec/PlistBuddy -c \"Print :CFBundleShortVersionString\" \"$infoPlistPath\" 2>/dev/null\nif [ $? != 0 ]; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleShortVersionString string $versionNumber\" \"$infoPlistPath\"\nelse\n /usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $versionNumber\" \"$infoPlistPath\"\nfi\n\ncript file from your workspace to insert its path.\n"; + }; F0C8BA10A27CA77E18F842E7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -875,8 +993,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E612EC352D0F07A80022720C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + E612EC472D0F07AD0022720C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E612EC382D0F07A80022720C /* HomePlayerWidgetExtension */; + targetProxy = E612EC462D0F07AD0022720C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -953,6 +1086,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1082,6 +1216,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1105,6 +1240,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1127,6 +1263,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1150,6 +1287,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1172,6 +1310,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1249,6 +1388,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1272,6 +1412,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1347,6 +1488,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1369,6 +1511,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1441,6 +1584,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1463,6 +1607,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1485,6 +1630,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1508,6 +1654,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1531,6 +1678,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1553,6 +1701,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1575,6 +1724,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1597,6 +1747,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1674,6 +1825,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1697,6 +1849,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1720,6 +1873,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1795,6 +1949,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1817,6 +1972,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1839,6 +1995,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1911,6 +2068,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1933,6 +2091,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1955,6 +2114,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -1977,6 +2137,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2000,6 +2161,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2023,6 +2185,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2046,6 +2209,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2068,6 +2232,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2090,6 +2255,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2112,6 +2278,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2134,6 +2301,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2156,6 +2324,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2233,6 +2402,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2256,6 +2426,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2279,6 +2450,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2302,6 +2474,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2377,6 +2550,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2399,6 +2573,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2421,6 +2596,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2443,6 +2619,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2515,6 +2692,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2537,6 +2715,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = stable.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2559,6 +2738,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = dev.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2581,6 +2761,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = nightly.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 88NVGSJ5N3; ENABLE_BITCODE = NO; @@ -2597,6 +2778,498 @@ }; name = "Profile-nightly"; }; + E612EC4A2D0F07AD0022720C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E612EC4B2D0F07AD0022720C /* Debug-nightly */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Debug-nightly"; + }; + E612EC4C2D0F07AD0022720C /* Debug-dev */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Debug-dev"; + }; + E612EC4D2D0F07AD0022720C /* Debug-stable */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Debug-stable"; + }; + E612EC4E2D0F07AD0022720C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + E612EC4F2D0F07AD0022720C /* Release-nightly */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Release-nightly"; + }; + E612EC502D0F07AD0022720C /* Release-dev */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Release-dev"; + }; + E612EC512D0F07AD0022720C /* Release-stable */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Release-stable"; + }; + E612EC522D0F07AD0022720C /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; + E612EC532D0F07AD0022720C /* Profile-nightly */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Profile-nightly"; + }; + E612EC542D0F07AD0022720C /* Profile-dev */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Profile-dev"; + }; + E612EC552D0F07AD0022720C /* Profile-stable */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = HomePlayerWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 88NVGSJ5N3; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HomePlayerWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = HomePlayerWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = oss.krtirtho.spotube.HomePlayerWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Profile-stable"; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -2695,6 +3368,25 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + E612EC572D0F07AD0022720C /* Build configuration list for PBXNativeTarget "HomePlayerWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E612EC4A2D0F07AD0022720C /* Debug */, + E612EC4B2D0F07AD0022720C /* Debug-nightly */, + E612EC4C2D0F07AD0022720C /* Debug-dev */, + E612EC4D2D0F07AD0022720C /* Debug-stable */, + E612EC4E2D0F07AD0022720C /* Release */, + E612EC4F2D0F07AD0022720C /* Release-nightly */, + E612EC502D0F07AD0022720C /* Release-dev */, + E612EC512D0F07AD0022720C /* Release-stable */, + E612EC522D0F07AD0022720C /* Profile */, + E612EC532D0F07AD0022720C /* Profile-nightly */, + E612EC542D0F07AD0022720C /* Profile-dev */, + E612EC552D0F07AD0022720C /* Profile-stable */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements new file mode 100644 index 00000000..58165678 --- /dev/null +++ b/ios/Runner/Runner.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.spotube_home_player_widget + + + diff --git a/ios/dev.entitlements b/ios/dev.entitlements new file mode 100644 index 00000000..58165678 --- /dev/null +++ b/ios/dev.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.spotube_home_player_widget + + + diff --git a/ios/nightly.entitlements b/ios/nightly.entitlements new file mode 100644 index 00000000..58165678 --- /dev/null +++ b/ios/nightly.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.spotube_home_player_widget + + + diff --git a/ios/stable.entitlements b/ios/stable.entitlements new file mode 100644 index 00000000..58165678 --- /dev/null +++ b/ios/stable.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.spotube_home_player_widget + + + diff --git a/lib/main.dart b/lib/main.dart index f13991e2..fa567129 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'package:flutter_discord_rpc/flutter_discord_rpc.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:hive/hive.dart'; +import 'package:home_widget/home_widget.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:local_notifier/local_notifier.dart'; import 'package:media_kit/media_kit.dart'; @@ -27,6 +28,7 @@ import 'package:spotube/hooks/configurators/use_has_touch.dart'; import 'package:spotube/models/database/database.dart'; import 'package:spotube/provider/audio_player/audio_player_streams.dart'; import 'package:spotube/provider/database/database.dart'; +import 'package:spotube/provider/glance/glance.dart'; import 'package:spotube/provider/server/bonsoir.dart'; import 'package:spotube/provider/server/server.dart'; import 'package:spotube/provider/tray_manager/tray_manager.dart'; @@ -115,6 +117,10 @@ Future main(List rawArgs) async { await WindowManagerTools.initialize(); } + if (kIsIOS) { + HomeWidget.setAppGroupId("group.spotube_home_player_widget"); + } + runApp( ProviderScope( overrides: [ @@ -161,6 +167,10 @@ class Spotube extends HookConsumerWidget { useEffect(() { FlutterNativeSplash.remove(); + if (kIsMobile) { + HomeWidget.registerInteractivityCallback(glanceBackgroundCallback); + } + return () { /// For enabling hot reload for audio player if (!kDebugMode) return; diff --git a/lib/pages/root/root_app.dart b/lib/pages/root/root_app.dart index 0274de00..2a6c36f0 100644 --- a/lib/pages/root/root_app.dart +++ b/lib/pages/root/root_app.dart @@ -18,6 +18,7 @@ import 'package:spotube/hooks/configurators/use_endless_playback.dart'; import 'package:spotube/pages/home/home.dart'; import 'package:spotube/provider/download_manager_provider.dart'; import 'package:spotube/provider/audio_player/audio_player.dart'; +import 'package:spotube/provider/glance/glance.dart'; import 'package:spotube/provider/server/routes/connect.dart'; import 'package:spotube/services/connectivity_adapter.dart'; import 'package:spotube/utils/platform.dart'; @@ -39,6 +40,8 @@ class RootApp extends HookConsumerWidget { final scaffoldMessenger = ScaffoldMessenger.of(context); final connectRoutes = ref.watch(serverConnectRoutesProvider); + ref.listen(glanceProvider, (_, __) {}); + useEffect(() { WidgetsBinding.instance.addPostFrameCallback((_) async { ServiceUtils.checkForUpdates(context, ref); diff --git a/lib/provider/glance/glance.dart b/lib/provider/glance/glance.dart new file mode 100644 index 00000000..22faa13f --- /dev/null +++ b/lib/provider/glance/glance.dart @@ -0,0 +1,169 @@ +import 'dart:convert'; + +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:home_widget/home_widget.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:http/http.dart'; +import 'package:logger/logger.dart'; +import 'package:spotify/spotify.dart'; +import 'package:spotube/provider/audio_player/audio_player.dart'; +import 'package:spotube/provider/server/server.dart'; +import 'package:spotube/services/audio_player/audio_player.dart'; +import 'package:spotube/services/logger/logger.dart'; +import 'package:spotube/utils/platform.dart'; + +@pragma("vm:entry-point") +Future glanceBackgroundCallback(Uri? data) async { + final logger = Logger(); + try { + if (data == null || + data.host != "playback" || + data.pathSegments.isEmpty || + data.queryParameters["serverAddress"] == null) { + return; + } + + final command = data.pathSegments.first; + final res = await get( + Uri.parse( + "http://${data.queryParameters["serverAddress"]}/playback/$command", + ), + ); + + if (res.statusCode != 200) { + throw Exception("Failed to execute command: $command\nBody: ${res.body}"); + } + } catch (e) { + logger.e("[GlanceBackgroundCallback] $e"); + } +} + +Future _saveWidgetData(String key, T? value) async { + try { + if (!kIsMobile) return null; + + return await HomeWidget.saveWidgetData(key, value); + } catch (e, stack) { + AppLogger.reportError(e, stack); + return null; + } +} + +Future _updateWidget() async { + try { + if (!kIsMobile) return; + + if (kIsAndroid) { + await HomeWidget.updateWidget( + androidName: 'HomePlayerWidgetReceiver', + qualifiedAndroidName: + 'oss.krtirtho.spotube.glance.HomePlayerWidgetReceiver', + ); + } + if (kIsIOS) { + await HomeWidget.updateWidget( + name: 'HomePlayerWidget', + iOSName: 'HomePlayerWidget', + ); + } + } on Exception catch (e, stack) { + AppLogger.reportError(e, stack); + } +} + +Future _sendActiveTrack(Track? track) async { + if (track == null) { + await _saveWidgetData("activeTrack", null); + await _updateWidget(); + return; + } + + final jsonTrack = track.toJson(); + + final image = track.album?.images?.first; + final cachedImage = await DefaultCacheManager().getSingleFile(image!.url!); + final data = { + ...jsonTrack, + "album": { + ...jsonTrack["album"], + "images": [ + { + ...image.toJson(), + "path": cachedImage.path, + } + ] + } + }; + + await _saveWidgetData("activeTrack", jsonEncode(data)); + + await _updateWidget(); +} + +final glanceProvider = Provider((ref) { + final server = ref.read(serverProvider); + final activeTrack = ref.read(audioPlayerProvider).activeTrack; + + server.whenData( + (value) async { + final (:server, :port) = value; + + await _saveWidgetData( + "playbackServerAddress", + "${server.address.host}:$port", + ); + await _updateWidget(); + }, + ); + + _sendActiveTrack(activeTrack); + + ref.listen(serverProvider, (prev, next) async { + next.whenData( + (value) async { + final (:server, :port) = value; + + await _saveWidgetData( + "playbackServerAddress", + "${server.address.host}:$port", + ); + await _updateWidget(); + }, + ); + }); + + ref.listen( + audioPlayerProvider, + (previous, next) async { + try { + if (previous?.activeTrack != next.activeTrack && + next.activeTrack != null) { + await _sendActiveTrack(next.activeTrack); + } + } catch (e, stack) { + AppLogger.reportError(e, stack); + } + }, + ); + + final subscriptions = [ + audioPlayer.playingStream.listen((playing) async { + await _saveWidgetData("isPlaying", playing); + await _updateWidget(); + }), + audioPlayer.positionStream.listen((position) async { + await _saveWidgetData("position", position.inSeconds); + await _updateWidget(); + }), + audioPlayer.durationStream.listen((duration) async { + await _saveWidgetData("duration", duration.inSeconds); + await _updateWidget(); + }), + ]; + + ref.onDispose(() { + for (final subscription in subscriptions) { + subscription.cancel(); + } + }); +}); diff --git a/lib/provider/server/router.dart b/lib/provider/server/router.dart index e2a579cc..06ff4a24 100644 --- a/lib/provider/server/router.dart +++ b/lib/provider/server/router.dart @@ -14,6 +14,10 @@ final serverRouterProvider = Provider((ref) { router.get("/stream/", playbackRoutes.getStreamTrackId); + router.get("/playback/toggle-playback", playbackRoutes.togglePlayback); + router.get("/playback/previous", playbackRoutes.previousTrack); + router.get("/playback/next", playbackRoutes.nextTrack); + router.all("/ws", connectRoutes.websocket); return router; diff --git a/lib/provider/server/routes/playback.dart b/lib/provider/server/routes/playback.dart index 34317aa1..289da0e3 100644 --- a/lib/provider/server/routes/playback.dart +++ b/lib/provider/server/routes/playback.dart @@ -188,6 +188,27 @@ class ServerPlaybackRoutes { return Response.internalServerError(); } } + + /// @get('/playback/toggle-playback') + Future togglePlayback(Request request) async { + audioPlayer.isPlaying + ? await audioPlayer.pause() + : await audioPlayer.resume(); + + return Response.ok("Playback toggled"); + } + + /// @get('/playback/previous') + Future previousTrack(Request request) async { + await audioPlayer.skipToPrevious(); + return Response.ok("Previous track"); + } + + /// @get('/playback/next') + Future nextTrack(Request request) async { + await audioPlayer.skipToNext(); + return Response.ok("Next track"); + } } final serverPlaybackRoutesProvider = diff --git a/pubspec.lock b/pubspec.lock index e6e365a6..5aa4f9b4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,10 +42,10 @@ packages: dependency: "direct main" description: name: app_links - sha256: "433df2e61b10519407475d7f69e470789d23d593f28224c38ba1068597be7950" + sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99 url: "https://pub.dev" source: hosted - version: "6.3.3" + version: "6.3.2" app_links_linux: dependency: transitive description: @@ -1095,6 +1095,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + home_widget: + dependency: "direct main" + description: + name: home_widget + sha256: b313e3304c0429669fddf1286e1fbf61a64b873f38ba30b3eb890ef0d7560b12 + url: "https://pub.dev" + source: hosted + version: "0.7.0" hooks_riverpod: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 072c9a0a..3866e410 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,6 +71,7 @@ dependencies: google_fonts: ^6.2.1 hive: ^2.2.3 hive_flutter: ^1.1.0 + home_widget: ^0.7.0 hooks_riverpod: ^2.5.1 html: ^0.15.1 html_unescape: ^2.0.0 @@ -162,6 +163,7 @@ flutter: - assets/ - assets/tutorial/ - assets/logos/ + - assets/backgrounds/ - LICENSE flutter_gen: